<commit_before>import React, { useContext, memo } from "react";
import { Text, View, StyleSheet, TouchableOpacity, Image } from "react-native";
import PropTypes from "prop-types";
import { FontAwesome } from "@expo/vector-icons";
import { bodyFormat, dateFormat, bodySearchUrl, emojisArrayToObject } from "../util/parser";
import CustomEmoji from "react-native-customemoji";

import t from "../services/I18n";

import Reply from "./item/Reply";
import Boost from "./item/Boost";
import Favourite from "./item/Favourite";
import Action from "./item/Action";

import { ThemeContext } from "react-native-elements";
import MastoRowBody from "./MastoRowBody";
import Detail from "./item/Detail";

const MastoRow = ({ navigation, item, current, actions }) => {
    // Toot data
    let { id, created_at, sensitive, reblog, account, content, reblogged, reblogs_count, favourited, uri, url, favourites_count, visibility, emojis} = item;
    // current
    let { user_credentials, domain, access_token, notification_count, instance } = current;
    // Actions
    let { ReplyAction, BoostAction, FavouriteAction, HideAction, DeleteAction, DetailAction } = actions;
    // Theme
    const { theme } = useContext(ThemeContext);
    let rebloggedName = "";
    let tootID = id;
    if(reblog){
        rebloggedName = account.display_name !== "" ? account.display_name : account.username;
        tootID = reblog.id;
        content = reblog.content;
        account = reblog.account;
    }
    let myself = user_credentials && user_credentials.acct === account.acct;
    return (
        <View key={id} style={[styles.container,{backgroundColor: theme.customColors.charBackground}]}>
            { reblog &&
                <View style={styles.isReplyContainer}>
                    <View style={{flex:0.18, borderWidth:0, alignItems:"flex-end"}}>
                        <FontAwesome name={"retweet"} size={16} color={theme.customColors.item.boost} style={{marginRight:5}}/>
                    </View>
                    <Text style={{flex:0.82, color: theme.colors.grey0}} ellipsizeMode="tail" numberOfLines={1}>{rebloggedName + t("notifications.boosted")} </Text>
                </View>
            }
            <View style={styles.innerContainer}>
                <View style={styles.photoContainer}>
                    <View style={styles.innerPhotoContainer}>
                        <TouchableOpacity
                            onPress={() => navigation.navigate("Profile")}>
                            <Image
                                source={{uri: account.avatar}}
                                style={styles.photo}/>
                        </TouchableOpacity>
                    </View>
                </View>
                <View style={styles.info}>
                    <View style={styles.userDetails}>
                        <CustomEmoji emojis={emojisArrayToObject(account.emojis)}>
                            <Text style={styles.userName} ellipsizeMode="tail" numberOfLines={1}>{account.display_name !== "" ? account.display_name : account.username}
                                <Text style={styles.userHandleAndTime}>{" @"+account.acct}</Text>
                            </Text>
                        </CustomEmoji>
                    </View>
                    <View style={styles.tootTextContainer}>
                        <MastoRowBody content={content} linkStyle={{color: theme.customColors.link}} style={styles.tootText} emojis={emojis} sensitive={sensitive} />
                    </View>
                    <View style={styles.item}>
                        <Reply
                            id={id}
                            tootid={tootID}
                            user={account.display_name !== "" ? account.display_name : account.username}
                            acct={account.acct}
                            image={account.avatar}
                            body={content}
                            style={styles.itemFlex}
                            onReply={ReplyAction}
                        />
                        <Boost
                            id={id}
                            tootid={tootID}
                            reblogged={reblogged}
                            count={reblogs_count}
                            style={styles.itemFlex}
                            onBoost={BoostAction}
                            disabled={visibility === "private" || visibility === "direct"}
                        />
                        <Favourite
                            id={id}
                            tootid={tootID}
                            favourited={favourited}
                            count={favourites_count}
                            style={styles.itemFlex}
                            onFavourite={FavouriteAction}
                        />
                        <Action
                            id={id}
                            tootid={tootID}
                            style={styles.itemFlex}
                            url={url}
                            account_url={account.url}
                            user={account.display_name !== "" ? account.display_name : account.username}
                            acct={account.acct}
                            image={account.avatar}
                            body={content}
                            myself={myself}
                            onReply={ReplyAction}
                            onHide={HideAction}
                            onDeleting={DeleteAction}
                        />
                        <Detail date={created_at} style={[styles.itemFlex,{fontWeight: "bold", fontSize: 15, color: theme.customColors.item.none}]} />
                    </View>
                </View>
            </View>
        </View>
    );
};
MastoRow.propTypes = {
    navigation: PropTypes.any,
    item: PropTypes.shape(
        {
            id: PropTypes.string.isRequired,
            created_at: PropTypes.string,
            in_reply_to_id: PropTypes.string,
            in_reply_to_account_id: PropTypes.string,
            sensitive: PropTypes.bool,
            spoiler_text: PropTypes.string,
            visibility: PropTypes.string,
            uri: PropTypes.string,
            url: PropTypes.string,
            replies_count: PropTypes.number,
            reblogs_count: PropTypes.number,
            favourites_count: PropTypes.number,
            favourited: PropTypes.bool,
            reblogged: PropTypes.bool,
            muted: PropTypes.bool,
            bookmarked: PropTypes.bool,
            content: PropTypes.string,
            reblog: PropTypes.any,
            application: PropTypes.object,
            account: PropTypes.object,
            media_attachments: PropTypes.array,
            mentions: PropTypes.array,
            tags: PropTypes.array,
            emojis: PropTypes.array,
            card: PropTypes.object,
            poll: PropTypes.any,
        }
    ),
    current: PropTypes.shape(
        {
            user_credentials: PropTypes.object,
            domain: PropTypes.string,
            access_token: PropTypes.string,
            notification_count: PropTypes.number,
            instance: PropTypes.object
        }
    ),
    actions: PropTypes.object
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        paddingTop: 8,
        paddingBottom: 5
    },
    isReplyContainer: {
        flex: 1,
        flexDirection: "row",
        borderWidth: 0,
        height: 20,
        marginTop: 2
    },
    innerContainer: {
        flex: 1,
        borderColor: "green",
        flexDirection: "row",
        borderWidth: 0,
        height: "auto"
    },
    photoContainer: {
        flex: 0.18,
        borderColor: "yellow",
        flexDirection: "column",
        borderWidth: 0
    },
    innerPhotoContainer: {
        height: 50, alignItems: "center"
    },
    photo: {
        width: 50,
        height: 50,
        borderRadius: 5
    },
    info: {
        flex: 0.82,
        borderColor: "yellow",
        flexDirection: "column",
        borderWidth: 0
    },
    userDetails: {
        borderWidth: 0
    },
    userName: {
        color: "black",
        fontWeight: "bold",
        fontSize: 16,
    },
    userHandleAndTime: {
        color: "#8899a6",
        fontWeight: "normal",
        fontSize: 15,
    },
    tootTextContainer: {
        flex: 1,
        borderWidth: 0,
        marginTop:2,
    },
    tootText: {
        color: "black",
        paddingTop: 2,
        paddingRight: 10,
        fontSize: 16,
    },
    tootActionsContainer: {
        flex: 1,
        borderColor: "blue",
        borderWidth: 0,
        marginTop: 5,
        flexDirection: "row",
        paddingBottom: 5
    },
    item: {
        flex: 1,
        paddingRight:10,
        paddingTop: 5,
        paddingBottom: 5,
        flexDirection: "row"
    },
    itemFlex: {
        flex: 1,
        paddingRight: 10,
    }
});
export default MastoRow;<commit_msg>useMemo<commit_after>import React, { useContext, useMemo } from "react";
import { Text, View, StyleSheet, TouchableOpacity, Image } from "react-native";
import PropTypes from "prop-types";
import { FontAwesome } from "@expo/vector-icons";
import { bodyFormat, dateFormat, bodySearchUrl, emojisArrayToObject } from "../util/parser";
import CustomEmoji from "react-native-customemoji";

import t from "../services/I18n";

import Reply from "./item/Reply";
import Boost from "./item/Boost";
import Favourite from "./item/Favourite";
import Action from "./item/Action";

import { ThemeContext } from "react-native-elements";
import MastoRowBody from "./MastoRowBody";
import Detail from "./item/Detail";

const MastoRow = ({ navigation, item, current, actions }) => {
    // Toot data
    let { id, created_at, sensitive, reblog, account, content, reblogged, reblogs_count, favourited, uri, url, favourites_count, visibility, emojis} = item;
    // current
    let { user_credentials, domain, access_token, notification_count, instance } = current;
    // Actions
    let { ReplyAction, BoostAction, FavouriteAction, HideAction, DeleteAction, DetailAction } = actions;
    // Theme
    const { theme } = useContext(ThemeContext);
    let rebloggedName = "";
    let tootID = id;
    if(reblog){
        rebloggedName = account.display_name !== "" ? account.display_name : account.username;
        tootID = reblog.id;
        content = reblog.content;
        account = reblog.account;
    }
    let myself = user_credentials && user_credentials.acct === account.acct;
    return (
        <View key={id} style={[styles.container,{backgroundColor: theme.customColors.charBackground}]}>
            { reblog && useMemo(() =>
                <View style={styles.isReplyContainer}>
                    <View style={{flex:0.18, borderWidth:0, alignItems:"flex-end"}}>
                        <FontAwesome name={"retweet"} size={16} color={theme.customColors.item.boost} style={{marginRight:5}}/>
                    </View>
                    <Text style={{flex:0.82, color: theme.colors.grey0}} ellipsizeMode="tail" numberOfLines={1}>{rebloggedName + t("notifications.boosted")} </Text>
                </View>
            , [rebloggedName])}
            <View style={styles.innerContainer}>
                <View style={styles.photoContainer}>
                    { useMemo(() =>
                        <View style={styles.innerPhotoContainer}>
                            <TouchableOpacity
                                onPress={() => navigation.navigate("Profile")}>
                                <Image
                                    source={{uri: account.avatar}}
                                    style={styles.photo}/>
                            </TouchableOpacity>
                        </View>
                    , [account])}
                </View>
                <View style={styles.info}>
                    { useMemo(() =>
                        <View style={styles.userDetails}>
                            <CustomEmoji emojis={emojisArrayToObject(account.emojis)}>
                                <Text style={styles.userName} ellipsizeMode="tail" numberOfLines={1}>{account.display_name !== "" ? account.display_name : account.username}
                                    <Text style={styles.userHandleAndTime}>{" @"+account.acct}</Text>
                                </Text>
                            </CustomEmoji>
                        </View>
                    , [account])
                    }
                    <View style={styles.tootTextContainer}>
                        <MastoRowBody content={content} linkStyle={{color: theme.customColors.link}} style={styles.tootText} emojis={emojis} sensitive={sensitive} />
                    </View>
                    <View style={styles.item}>
                        <Reply
                            id={id}
                            tootid={tootID}
                            user={account.display_name !== "" ? account.display_name : account.username}
                            acct={account.acct}
                            image={account.avatar}
                            body={content}
                            style={styles.itemFlex}
                            onReply={ReplyAction}
                        />
                        <Boost
                            id={id}
                            tootid={tootID}
                            reblogged={reblogged}
                            count={reblogs_count}
                            style={styles.itemFlex}
                            onBoost={BoostAction}
                            disabled={visibility === "private" || visibility === "direct"}
                        />
                        <Favourite
                            id={id}
                            tootid={tootID}
                            favourited={favourited}
                            count={favourites_count}
                            style={styles.itemFlex}
                            onFavourite={FavouriteAction}
                        />
                        <Action
                            id={id}
                            tootid={tootID}
                            style={styles.itemFlex}
                            url={url}
                            account_url={account.url}
                            user={account.display_name !== "" ? account.display_name : account.username}
                            acct={account.acct}
                            image={account.avatar}
                            body={content}
                            myself={myself}
                            onReply={ReplyAction}
                            onHide={HideAction}
                            onDeleting={DeleteAction}
                        />
                        <Detail date={created_at} style={[styles.itemFlex,{fontWeight: "bold", fontSize: 15, color: theme.customColors.item.none}]} />
                    </View>
                </View>
            </View>
        </View>
    );
};
MastoRow.propTypes = {
    navigation: PropTypes.any,
    item: PropTypes.shape(
        {
            id: PropTypes.string.isRequired,
            created_at: PropTypes.string,
            in_reply_to_id: PropTypes.string,
            in_reply_to_account_id: PropTypes.string,
            sensitive: PropTypes.bool,
            spoiler_text: PropTypes.string,
            visibility: PropTypes.string,
            uri: PropTypes.string,
            url: PropTypes.string,
            replies_count: PropTypes.number,
            reblogs_count: PropTypes.number,
            favourites_count: PropTypes.number,
            favourited: PropTypes.bool,
            reblogged: PropTypes.bool,
            muted: PropTypes.bool,
            bookmarked: PropTypes.bool,
            content: PropTypes.string,
            reblog: PropTypes.any,
            application: PropTypes.object,
            account: PropTypes.object,
            media_attachments: PropTypes.array,
            mentions: PropTypes.array,
            tags: PropTypes.array,
            emojis: PropTypes.array,
            card: PropTypes.object,
            poll: PropTypes.any,
        }
    ),
    current: PropTypes.shape(
        {
            user_credentials: PropTypes.object,
            domain: PropTypes.string,
            access_token: PropTypes.string,
            notification_count: PropTypes.number,
            instance: PropTypes.object
        }
    ),
    actions: PropTypes.object
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
        paddingTop: 8,
        paddingBottom: 5
    },
    isReplyContainer: {
        flex: 1,
        flexDirection: "row",
        borderWidth: 0,
        height: 20,
        marginTop: 2
    },
    innerContainer: {
        flex: 1,
        borderColor: "green",
        flexDirection: "row",
        borderWidth: 0,
        height: "auto"
    },
    photoContainer: {
        flex: 0.18,
        borderColor: "yellow",
        flexDirection: "column",
        borderWidth: 0
    },
    innerPhotoContainer: {
        height: 50, alignItems: "center"
    },
    photo: {
        width: 50,
        height: 50,
        borderRadius: 5
    },
    info: {
        flex: 0.82,
        borderColor: "yellow",
        flexDirection: "column",
        borderWidth: 0
    },
    userDetails: {
        borderWidth: 0
    },
    userName: {
        color: "black",
        fontWeight: "bold",
        fontSize: 16,
    },
    userHandleAndTime: {
        color: "#8899a6",
        fontWeight: "normal",
        fontSize: 15,
    },
    tootTextContainer: {
        flex: 1,
        borderWidth: 0,
        marginTop:2,
    },
    tootText: {
        color: "black",
        paddingTop: 2,
        paddingRight: 10,
        fontSize: 16,
    },
    tootActionsContainer: {
        flex: 1,
        borderColor: "blue",
        borderWidth: 0,
        marginTop: 5,
        flexDirection: "row",
        paddingBottom: 5
    },
    item: {
        flex: 1,
        paddingRight:10,
        paddingTop: 5,
        paddingBottom: 5,
        flexDirection: "row"
    },
    itemFlex: {
        flex: 1,
        paddingRight: 10,
    }
});
export default MastoRow;<|endoftext|>


<commit_before>import {Fragment, emptyFragment} from "./fragment"
import {sameMarks} from "./mark"

const emptyArray = []

/**
 * Document node class
 */
export class Node {
  constructor(type, attrs, content, marks) {
    if (content && !(content instanceof Fragment)) throw new Error("OH NO")
    this.type = type
    this.attrs = attrs
    this.content = content || emptyFragment
    this.marks = marks || emptyArray
  }

  get size() { return this.content.size }

  get width() { return 1 }

  child(off) { return this.content.child(off) }

  iter(start, end) { return this.content.iter(start, end) }
  reverseIter(start, end) { return this.content.reverseIter(start, end) }

  chunkBefore(off) { return this.content.chunkBefore(off) }
  chunkAfter(off) { return this.content.chunkAfter(off) }

  forEach(f) { this.content.forEach(f) }

  get textContent() { return this.content.textContent }

  get firstChild() { return this.content.firstChild }
  get lastChild() { return this.content.lastChild }

  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  hasMarkup(type, attrs, marks) {
    return this.type == type && Node.sameAttrs(this.attrs, attrs) && sameMarks(this.marks, marks || emptyArray)
  }

  static sameAttrs(a, b) {
    if (a == b) return true
    let empty = isEmpty(a)
    if (empty != isEmpty(b)) return false
    if (!empty) for (var prop in a)
      if (a[prop] !== b[prop]) return false
    return true
  }

  copy(content = null) {
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  mark(marks) {
    return new this.constructor(this.type, this.attrs, this.content, marks)
  }

  slice(from, to) {
    return this.copy(this.content.slice(from, to))
  }

  splice(from, to, replace) {
    return this.copy(this.content.slice(0, from).append(replace).append(this.content.slice(to)))
  }

  append(slice, joinLeft = 0, joinRight = 0) {
    return this.copy(this.content.append(slice, joinLeft, joinRight))
  }

  replace(pos, node) {
    return this.copy(this.content.replace(pos, node))
  }

  replaceDeep(path, node, depth = 0) {
    if (depth == path.length) return node
    let pos = path[depth]
    return this.replace(pos, this.child(pos).replaceDeep(path, node, depth + 1))
  }

  close(depth, side) {
    if (depth == 0 && this.size == 0 && !this.type.canBeEmpty)
      return this.copy(this.type.defaultContent())
    let closedContent
    if (depth > 0 && (closedContent = this.content.close(depth - 1, side)) != this.content)
      return this.copy(closedContent)
    return this
  }

  /**
   * Get a child node given a path.
   *
   * @param  {array} path
   * @return {Node}
   */
  path(path) {
    for (var i = 0, node = this; i < path.length; node = node.child(path[i]), i++) {}
    return node
  }

  nodeAfter(pos) {
    return this.path(pos.path).child(pos.offset)
  }

  pathNodes(path) {
    let nodes = []
    for (var i = 0, node = this;; i++) {
      nodes.push(node)
      if (i == path.length) break
      node = node.child(path[i])
    }
    return nodes
  }

  isValidPos(pos, requireTextblock) {
    for (let i = 0, node = this;; i++) {
      if (i == pos.path.length) {
        if (requireTextblock && !node.isTextblock) return false
        return pos.offset <= node.size
      } else {
        let n = pos.path[i]
        if (n >= node.size) return false
        node = node.child(n)
      }
    }
  }

  nodesBetween(from, to, f, path = [], parent = null) {
    if (f(this, path, parent) === false) return
    this.content.nodesBetween(from, to, f, path, this)
  }

  inlineNodesBetween(from, to, f) {
    this.nodesBetween(from, to, (node, path, parent) => {
      if (node.isInline) {
        let last = path.length - 1
        f(node, path.slice(0, last), path[last], path[last] + node.width, parent)
      }
    })
  }

  sliceBetween(from, to, depth = 0) {
    return this.copy(this.content.sliceBetween(from, to, depth))
  }

  get isBlock() { return this.type.isBlock }
  get isTextblock() { return this.type.isTextblock }
  get isInline() { return this.type.isInline }
  get isText() { return this.type.isText }

  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toString() + ")"
    return wrapMarks(this.marks, name)
  }

  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks
    return obj
  }

  // This is a hack to be able to treat a node object as an iterator result
  get value() { return this }

  static fromJSON(schema, json) {
    let type = schema.nodeType(json.type)
    let content = json.text != null ? json.text : Fragment.fromJSON(schema, json.content)
    return type.create(json.attrs, content, json.marks && json.marks.map(schema.markFromJSON))
  }
}

if (typeof Symbol != "undefined") Node.prototype[Symbol.iterator] = function() { return this.iter() }

export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)
    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  get width() { return this.text.length }

  mark(marks) {
    return new TextNode(this.type, this.attrs, this.text, marks)
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}

function isEmpty(obj) {
  if (obj) for (let _ in obj) return false
  return true
}
<commit_msg>Test (and fix) JSON parsing/serialization<commit_after>import {Fragment, emptyFragment} from "./fragment"
import {sameMarks} from "./mark"

const emptyArray = []

/**
 * Document node class
 */
export class Node {
  constructor(type, attrs, content, marks) {
    if (content && !(content instanceof Fragment)) throw new Error("OH NO")
    this.type = type
    this.attrs = attrs
    this.content = content || emptyFragment
    this.marks = marks || emptyArray
  }

  get size() { return this.content.size }

  get width() { return 1 }

  child(off) { return this.content.child(off) }

  iter(start, end) { return this.content.iter(start, end) }
  reverseIter(start, end) { return this.content.reverseIter(start, end) }

  chunkBefore(off) { return this.content.chunkBefore(off) }
  chunkAfter(off) { return this.content.chunkAfter(off) }

  forEach(f) { this.content.forEach(f) }

  get textContent() { return this.content.textContent }

  get firstChild() { return this.content.firstChild }
  get lastChild() { return this.content.lastChild }

  sameMarkup(other) {
    return this.hasMarkup(other.type, other.attrs, other.marks)
  }

  hasMarkup(type, attrs, marks) {
    return this.type == type && Node.sameAttrs(this.attrs, attrs) && sameMarks(this.marks, marks || emptyArray)
  }

  static sameAttrs(a, b) {
    if (a == b) return true
    let empty = isEmpty(a)
    if (empty != isEmpty(b)) return false
    if (!empty) for (var prop in a)
      if (a[prop] !== b[prop]) return false
    return true
  }

  copy(content = null) {
    return new this.constructor(this.type, this.attrs, content, this.marks)
  }

  mark(marks) {
    return new this.constructor(this.type, this.attrs, this.content, marks)
  }

  slice(from, to) {
    return this.copy(this.content.slice(from, to))
  }

  splice(from, to, replace) {
    return this.copy(this.content.slice(0, from).append(replace).append(this.content.slice(to)))
  }

  append(slice, joinLeft = 0, joinRight = 0) {
    return this.copy(this.content.append(slice, joinLeft, joinRight))
  }

  replace(pos, node) {
    return this.copy(this.content.replace(pos, node))
  }

  replaceDeep(path, node, depth = 0) {
    if (depth == path.length) return node
    let pos = path[depth]
    return this.replace(pos, this.child(pos).replaceDeep(path, node, depth + 1))
  }

  close(depth, side) {
    if (depth == 0 && this.size == 0 && !this.type.canBeEmpty)
      return this.copy(this.type.defaultContent())
    let closedContent
    if (depth > 0 && (closedContent = this.content.close(depth - 1, side)) != this.content)
      return this.copy(closedContent)
    return this
  }

  /**
   * Get a child node given a path.
   *
   * @param  {array} path
   * @return {Node}
   */
  path(path) {
    for (var i = 0, node = this; i < path.length; node = node.child(path[i]), i++) {}
    return node
  }

  nodeAfter(pos) {
    return this.path(pos.path).child(pos.offset)
  }

  pathNodes(path) {
    let nodes = []
    for (var i = 0, node = this;; i++) {
      nodes.push(node)
      if (i == path.length) break
      node = node.child(path[i])
    }
    return nodes
  }

  isValidPos(pos, requireTextblock) {
    for (let i = 0, node = this;; i++) {
      if (i == pos.path.length) {
        if (requireTextblock && !node.isTextblock) return false
        return pos.offset <= node.size
      } else {
        let n = pos.path[i]
        if (n >= node.size) return false
        node = node.child(n)
      }
    }
  }

  nodesBetween(from, to, f, path = [], parent = null) {
    if (f(this, path, parent) === false) return
    this.content.nodesBetween(from, to, f, path, this)
  }

  inlineNodesBetween(from, to, f) {
    this.nodesBetween(from, to, (node, path, parent) => {
      if (node.isInline) {
        let last = path.length - 1
        f(node, path.slice(0, last), path[last], path[last] + node.width, parent)
      }
    })
  }

  sliceBetween(from, to, depth = 0) {
    return this.copy(this.content.sliceBetween(from, to, depth))
  }

  get isBlock() { return this.type.isBlock }
  get isTextblock() { return this.type.isTextblock }
  get isInline() { return this.type.isInline }
  get isText() { return this.type.isText }

  toString() {
    let name = this.type.name
    if (this.content.size)
      name += "(" + this.content.toString() + ")"
    return wrapMarks(this.marks, name)
  }

  toJSON() {
    let obj = {type: this.type.name}
    for (let _ in this.attrs) {
      obj.attrs = this.attrs
      break
    }
    if (this.size)
      obj.content = this.content.toJSON()
    if (this.marks.length)
      obj.marks = this.marks.map(n => n.toJSON())
    return obj
  }

  // This is a hack to be able to treat a node object as an iterator result
  get value() { return this }

  static fromJSON(schema, json) {
    let type = schema.nodeType(json.type)
    let content = json.text != null ? json.text : Fragment.fromJSON(schema, json.content)
    return type.create(json.attrs, content, json.marks && json.marks.map(schema.markFromJSON))
  }
}

if (typeof Symbol != "undefined") Node.prototype[Symbol.iterator] = function() { return this.iter() }

export class TextNode extends Node {
  constructor(type, attrs, content, marks) {
    super(type, attrs, null, marks)
    this.text = content
  }

  toString() { return wrapMarks(this.marks, JSON.stringify(this.text)) }

  get textContent() { return this.text }

  get width() { return this.text.length }

  mark(marks) {
    return new TextNode(this.type, this.attrs, this.text, marks)
  }

  toJSON() {
    let base = super.toJSON()
    base.text = this.text
    return base
  }
}

function wrapMarks(marks, str) {
  for (let i = marks.length - 1; i >= 0; i--)
    str = marks[i].type.name + "(" + str + ")"
  return str
}

function isEmpty(obj) {
  if (obj) for (let _ in obj) return false
  return true
}
<|endoftext|>


<commit_before>var region = require('caniuse-lite/dist/unpacker/region').default
var path = require('path')
var fs = require('fs')

var BrowserslistError = require('./error')

var IS_SECTION = /^\s*\[(.+)\]\s*$/
var CONFIG_PATTERN = /^browserslist-config-/
var SCOPED_CONFIG__PATTERN = /@[^./]+\/browserslist-config(-|$)/
var FORMAT = 'Browserslist config should be a string or an array ' +
             'of strings with browser queries'

var filenessCache = { }
var configCache = { }

function checkExtend (name) {
  var use = ' Use `dangerousExtend` option to disable.'
  if (!CONFIG_PATTERN.test(name) && !SCOPED_CONFIG__PATTERN.test(name)) {
    throw new BrowserslistError(
      'Browserslist config needs `browserslist-config-` prefix. ' + use)
  }
  if (name.indexOf('.') !== -1) {
    throw new BrowserslistError(
      '`.` not allowed in Browserslist config name. ' + use)
  }
  if (name.indexOf('node_modules') !== -1) {
    throw new BrowserslistError(
      '`node_modules` not allowed in Browserslist config.' + use)
  }
}

function isFile (file) {
  if (file in filenessCache) {
    return filenessCache[file]
  }
  var result = fs.existsSync(file) && fs.statSync(file).isFile()
  if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
    filenessCache[file] = result
  }
  return result
}

function eachParent (file, callback) {
  var loc = path.resolve(file)
  do {
    var result = callback(loc)
    if (typeof result !== 'undefined') return result
  } while (loc !== (loc = path.dirname(loc)))
  return undefined
}

function check (section) {
  if (Array.isArray(section)) {
    for (var i = 0; i < section.length; i++) {
      if (typeof section[i] !== 'string') {
        throw new BrowserslistError(FORMAT)
      }
    }
  } else if (typeof section !== 'string') {
    throw new BrowserslistError(FORMAT)
  }
}

function pickEnv (config, opts) {
  if (typeof config !== 'object') return config

  var name
  if (typeof opts.env === 'string') {
    name = opts.env
  } else if (process.env.BROWSERSLIST_ENV) {
    name = process.env.BROWSERSLIST_ENV
  } else if (process.env.NODE_ENV) {
    name = process.env.NODE_ENV
  } else {
    name = 'production'
  }

  return config[name] || config.defaults
}

function parsePackage (file) {
  var config = JSON.parse(fs.readFileSync(file))
  if (config.browserlist && !config.browserslist) {
    throw new BrowserslistError(
      '`browserlist` key instead of `browserslist` in ' + file)
  }
  var list = config.browserslist
  if (Array.isArray(list)) {
    list = { defaults: list }
  }

  for (var i in list) {
    check(list[i])
  }

  return list
}

module.exports = {
  loadQueries: function loadQueries (context, name) {
    if (!context.dangerousExtend) checkExtend(name)
    // eslint-disable-next-line security/detect-non-literal-require
    var queries = require(name)
    if (!Array.isArray(queries)) {
      throw new BrowserslistError(
        '`' + name + '` config exports not an array of queries')
    }
    return queries
  },

  getStat: function getStat (opts) {
    var stats
    if (opts.stats) {
      stats = opts.stats
    } else if (process.env.BROWSERSLIST_STATS) {
      stats = process.env.BROWSERSLIST_STATS
    } else if (opts.path && path.resolve && fs.existsSync) {
      stats = eachParent(opts.path, function (dir) {
        var file = path.join(dir, 'browserslist-stats.json')
        return isFile(file) ? file : undefined
      })
    }

    if (typeof stats === 'string') {
      try {
        stats = JSON.parse(fs.readFileSync(stats))
      } catch (e) {
        throw new BrowserslistError('Can\'t read ' + stats)
      }
    }

    if (stats && 'dataByBrowser' in stats) {
      stats = stats.dataByBrowser
    }

    return stats
  },

  loadConfig: function loadConfig (opts) {
    if (process.env.BROWSERSLIST) {
      return process.env.BROWSERSLIST
    } else if (opts.config || process.env.BROWSERSLIST_CONFIG) {
      var file = opts.config || process.env.BROWSERSLIST_CONFIG
      if (path.basename(file) === 'package.json') {
        return pickEnv(parsePackage(file), opts)
      } else {
        return pickEnv(module.exports.readConfig(file), opts)
      }
    } else if (opts.path) {
      return pickEnv(module.exports.findConfig(opts.path), opts)
    } else {
      return undefined
    }
  },

  loadCountry: function loadCountry (usage, country) {
    var code = country.replace(/[^\w-]/g, '')
    if (!usage[code]) {
      // eslint-disable-next-line security/detect-non-literal-require
      var compressed = require('caniuse-lite/data/regions/' + code + '.js')
      var data = region(compressed)
      usage[country] = { }
      for (var i in data) {
        for (var j in data[i]) {
          usage[country][i + ' ' + j] = data[i][j]
        }
      }
    }
  },

  parseConfig: function parseConfig (string) {
    var result = { defaults: [] }
    var sections = ['defaults']

    string.toString()
      .replace(/#[^\n]*/g, '')
      .split(/\n/)
      .map(function (line) {
        return line.trim()
      })
      .filter(function (line) {
        return line !== ''
      })
      .forEach(function (line) {
        if (IS_SECTION.test(line)) {
          sections = line.match(IS_SECTION)[1].trim().split(' ')
          sections.forEach(function (section) {
            if (result[section]) {
              throw new BrowserslistError(
                'Dublicate section ' + section + ' in Browserslist config')
            }
            result[section] = []
          })
        } else {
          sections.forEach(function (section) {
            result[section].push(line)
          })
        }
      })

    return result
  },

  readConfig: function readConfig (file) {
    if (!isFile(file)) {
      throw new BrowserslistError('Can\'t read ' + file + ' config')
    }
    return module.exports.parseConfig(fs.readFileSync(file))
  },

  findConfig: function findConfig (from) {
    from = path.resolve(from)

    var cacheKey = isFile(from) ? path.dirname(from) : from
    if (cacheKey in configCache) {
      return configCache[cacheKey]
    }

    var resolved = eachParent(from, function (dir) {
      var config = path.join(dir, 'browserslist')
      var pkg = path.join(dir, 'package.json')
      var rc = path.join(dir, '.browserslistrc')

      var pkgBrowserslist
      if (isFile(pkg)) {
        try {
          pkgBrowserslist = parsePackage(pkg)
        } catch (e) {
          if (e.name === 'BrowserslistError') throw e
          console.warn(
            '[Browserslist] Could not parse ' + pkg + '. Ignoring it.')
        }
      }

      if (isFile(config) && pkgBrowserslist) {
        throw new BrowserslistError(
          dir + ' contains both browserslist and package.json with browsers')
      } else if (isFile(rc) && pkgBrowserslist) {
        throw new BrowserslistError(
          dir + ' contains both .browserslistrc and package.json with browsers')
      } else if (isFile(config) && isFile(rc)) {
        throw new BrowserslistError(
          dir + ' contains both .browserslistrc and browserslist')
      } else if (isFile(config)) {
        return module.exports.readConfig(config)
      } else if (isFile(rc)) {
        return module.exports.readConfig(rc)
      } else {
        return pkgBrowserslist
      }
    })
    if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
      configCache[cacheKey] = resolved
    }
    return resolved
  },

  clearCaches: function clearCaches () {
    filenessCache = { }
    configCache = { }
  }
}
<commit_msg>Resolve shareable config from current dir<commit_after>var region = require('caniuse-lite/dist/unpacker/region').default
var path = require('path')
var fs = require('fs')

var BrowserslistError = require('./error')

var IS_SECTION = /^\s*\[(.+)\]\s*$/
var CONFIG_PATTERN = /^browserslist-config-/
var SCOPED_CONFIG__PATTERN = /@[^./]+\/browserslist-config(-|$)/
var FORMAT = 'Browserslist config should be a string or an array ' +
             'of strings with browser queries'

var filenessCache = { }
var configCache = { }

function checkExtend (name) {
  var use = ' Use `dangerousExtend` option to disable.'
  if (!CONFIG_PATTERN.test(name) && !SCOPED_CONFIG__PATTERN.test(name)) {
    throw new BrowserslistError(
      'Browserslist config needs `browserslist-config-` prefix. ' + use)
  }
  if (name.indexOf('.') !== -1) {
    throw new BrowserslistError(
      '`.` not allowed in Browserslist config name. ' + use)
  }
  if (name.indexOf('node_modules') !== -1) {
    throw new BrowserslistError(
      '`node_modules` not allowed in Browserslist config.' + use)
  }
}

function isFile (file) {
  if (file in filenessCache) {
    return filenessCache[file]
  }
  var result = fs.existsSync(file) && fs.statSync(file).isFile()
  if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
    filenessCache[file] = result
  }
  return result
}

function eachParent (file, callback) {
  var loc = path.resolve(file)
  do {
    var result = callback(loc)
    if (typeof result !== 'undefined') return result
  } while (loc !== (loc = path.dirname(loc)))
  return undefined
}

function check (section) {
  if (Array.isArray(section)) {
    for (var i = 0; i < section.length; i++) {
      if (typeof section[i] !== 'string') {
        throw new BrowserslistError(FORMAT)
      }
    }
  } else if (typeof section !== 'string') {
    throw new BrowserslistError(FORMAT)
  }
}

function pickEnv (config, opts) {
  if (typeof config !== 'object') return config

  var name
  if (typeof opts.env === 'string') {
    name = opts.env
  } else if (process.env.BROWSERSLIST_ENV) {
    name = process.env.BROWSERSLIST_ENV
  } else if (process.env.NODE_ENV) {
    name = process.env.NODE_ENV
  } else {
    name = 'production'
  }

  return config[name] || config.defaults
}

function parsePackage (file) {
  var config = JSON.parse(fs.readFileSync(file))
  if (config.browserlist && !config.browserslist) {
    throw new BrowserslistError(
      '`browserlist` key instead of `browserslist` in ' + file)
  }
  var list = config.browserslist
  if (Array.isArray(list)) {
    list = { defaults: list }
  }

  for (var i in list) {
    check(list[i])
  }

  return list
}

module.exports = {
  loadQueries: function loadQueries (context, name) {
    if (!context.dangerousExtend) checkExtend(name)
    // eslint-disable-next-line security/detect-non-literal-require
    var queries = require(require.resolve(name, { paths: ['.'] }))
    if (!Array.isArray(queries)) {
      throw new BrowserslistError(
        '`' + name + '` config exports not an array of queries')
    }
    return queries
  },

  getStat: function getStat (opts) {
    var stats
    if (opts.stats) {
      stats = opts.stats
    } else if (process.env.BROWSERSLIST_STATS) {
      stats = process.env.BROWSERSLIST_STATS
    } else if (opts.path && path.resolve && fs.existsSync) {
      stats = eachParent(opts.path, function (dir) {
        var file = path.join(dir, 'browserslist-stats.json')
        return isFile(file) ? file : undefined
      })
    }

    if (typeof stats === 'string') {
      try {
        stats = JSON.parse(fs.readFileSync(stats))
      } catch (e) {
        throw new BrowserslistError('Can\'t read ' + stats)
      }
    }

    if (stats && 'dataByBrowser' in stats) {
      stats = stats.dataByBrowser
    }

    return stats
  },

  loadConfig: function loadConfig (opts) {
    if (process.env.BROWSERSLIST) {
      return process.env.BROWSERSLIST
    } else if (opts.config || process.env.BROWSERSLIST_CONFIG) {
      var file = opts.config || process.env.BROWSERSLIST_CONFIG
      if (path.basename(file) === 'package.json') {
        return pickEnv(parsePackage(file), opts)
      } else {
        return pickEnv(module.exports.readConfig(file), opts)
      }
    } else if (opts.path) {
      return pickEnv(module.exports.findConfig(opts.path), opts)
    } else {
      return undefined
    }
  },

  loadCountry: function loadCountry (usage, country) {
    var code = country.replace(/[^\w-]/g, '')
    if (!usage[code]) {
      // eslint-disable-next-line security/detect-non-literal-require
      var compressed = require('caniuse-lite/data/regions/' + code + '.js')
      var data = region(compressed)
      usage[country] = { }
      for (var i in data) {
        for (var j in data[i]) {
          usage[country][i + ' ' + j] = data[i][j]
        }
      }
    }
  },

  parseConfig: function parseConfig (string) {
    var result = { defaults: [] }
    var sections = ['defaults']

    string.toString()
      .replace(/#[^\n]*/g, '')
      .split(/\n/)
      .map(function (line) {
        return line.trim()
      })
      .filter(function (line) {
        return line !== ''
      })
      .forEach(function (line) {
        if (IS_SECTION.test(line)) {
          sections = line.match(IS_SECTION)[1].trim().split(' ')
          sections.forEach(function (section) {
            if (result[section]) {
              throw new BrowserslistError(
                'Dublicate section ' + section + ' in Browserslist config')
            }
            result[section] = []
          })
        } else {
          sections.forEach(function (section) {
            result[section].push(line)
          })
        }
      })

    return result
  },

  readConfig: function readConfig (file) {
    if (!isFile(file)) {
      throw new BrowserslistError('Can\'t read ' + file + ' config')
    }
    return module.exports.parseConfig(fs.readFileSync(file))
  },

  findConfig: function findConfig (from) {
    from = path.resolve(from)

    var cacheKey = isFile(from) ? path.dirname(from) : from
    if (cacheKey in configCache) {
      return configCache[cacheKey]
    }

    var resolved = eachParent(from, function (dir) {
      var config = path.join(dir, 'browserslist')
      var pkg = path.join(dir, 'package.json')
      var rc = path.join(dir, '.browserslistrc')

      var pkgBrowserslist
      if (isFile(pkg)) {
        try {
          pkgBrowserslist = parsePackage(pkg)
        } catch (e) {
          if (e.name === 'BrowserslistError') throw e
          console.warn(
            '[Browserslist] Could not parse ' + pkg + '. Ignoring it.')
        }
      }

      if (isFile(config) && pkgBrowserslist) {
        throw new BrowserslistError(
          dir + ' contains both browserslist and package.json with browsers')
      } else if (isFile(rc) && pkgBrowserslist) {
        throw new BrowserslistError(
          dir + ' contains both .browserslistrc and package.json with browsers')
      } else if (isFile(config) && isFile(rc)) {
        throw new BrowserslistError(
          dir + ' contains both .browserslistrc and browserslist')
      } else if (isFile(config)) {
        return module.exports.readConfig(config)
      } else if (isFile(rc)) {
        return module.exports.readConfig(rc)
      } else {
        return pkgBrowserslist
      }
    })
    if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
      configCache[cacheKey] = resolved
    }
    return resolved
  },

  clearCaches: function clearCaches () {
    filenessCache = { }
    configCache = { }
  }
}
<|endoftext|>


<commit_before>'use strict'

/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 2016 SIRAP Group All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * plugin.js Tinymce plugin headersfooters
 * @file plugin.js
 * @module
 * @name tinycmce-plugin-headersfooters
 * @description
 * A plugin for tinymce WYSIWYG HTML editor that allow to insert headers and footers
 * It will may be used with requires tinymce-plugin-paginate the a near future, but not for now.
 * @link https://github.com/sirap-group/tinymce-plugin-headersfooters
 * @author Rémi Becheras
 * @author Groupe SIRAP
 * @license GNU GPL-v2 http://www.tinymce.com/license
 * @version 1.0.0
 */

/**
 * Tinymce library - injected by the plugin loader.
 * @external tinymce
 * @see {@link https://www.tinymce.com/docs/api/class/tinymce/|Tinymce API Reference}
 */
var tinymce = window.tinymce

/**
 * The jQuery plugin namespace - plugin dependency.
 * @external "jQuery.fn"
 * @see {@link http://learn.jquery.com/plugins/|jQuery Plugins}
 */
var $ = window.jQuery

var ui = require('./utils/ui')
var HeaderFooterFactory = require('./classes/HeaderFooterFactory')

// Add the plugin to the tinymce PluginManager
tinymce.PluginManager.add('headersfooters', tinymcePluginHeadersFooters)

/**
 * Tinymce plugin headers/footers
 * @function
 * @global
 * @param {tinymce.Editor} editor - The injected tinymce editor.
 * @returns void
 */
function tinymcePluginHeadersFooters (editor, url) {
  var headerFooterFactory

  var menuItems = {
    insertHeader: ui.createInsertHeaderMenuItem(),
    insertFooter: ui.createInsertFooterMenuItem(),
    removeHeader: ui.createRemoveHeaderMenuItem(),
    removeFooter: ui.createRemoveFooterMenuItem()
  }

  // add menu items
  editor.addMenuItem('insertHeader', menuItems.insertHeader)
  editor.addMenuItem('removeHeader', menuItems.removeHeader)
  editor.addMenuItem('insertFooter', menuItems.insertFooter)
  editor.addMenuItem('removeFooter', menuItems.removeFooter)

  editor.on('init', onInitHandler)
  editor.on('SetContent', onSetContent)

  /**
   * On init event handler. Instanciate the factory and initialize menu items states
   * @function
   * @inner
   * @returns void
   */
  function onInitHandler () {
    headerFooterFactory = new HeaderFooterFactory(editor)
    initMenuItems(headerFooterFactory, menuItems)
  }

  /**
   * On SetContent event handler. Load or reload headers and footers from existing elements if it should do.
   * @function
   * @inner
   * @returns void
   */
  function onSetContent (evt) {
    // var $bodyElmt = $('body', editor.getDoc())
    var content = $(editor.getBody()).html()
    var emptyContent = '

'
    if (content && content !== emptyContent) {
      if (headerFooterFactory) {
        reloadHeadFoots(menuItems)
      } else {
        setTimeout(reloadHeadFoots.bind(null, menuItems), 100)
      }
    } else {
      // evt.content = '
' + evt.content + '
'
    }
  }

  /**
   * Helper function. Do the reload of headers and footers
   * @function
   * @inner
   * @returns void
   */
  function reloadHeadFoots (menuItems) {
    var $headFootElmts = $('*[data-headfoot]', editor.getDoc())
    var $bodyElmt = $('*[data-headfoot-body]', editor.getDoc())
    var hasBody = !!$bodyElmt.length
    var $allElmts = null

    // init starting states
    menuItems.insertHeader.show()
    menuItems.insertFooter.show()
    menuItems.removeHeader.hide()
    menuItems.removeFooter.hide()

    // set another state and load elements if a header or a footer exists
    $headFootElmts.each(function (i, el) {
      var $el = $(el)
      if ($el.attr('data-headfoot-header')) {
        menuItems.insertHeader.hide()
        menuItems.removeHeader.show()
      } else if ($el.attr('data-headfoot-body')) {
        // @TODO something ?
      } else if ($el.attr('data-headfoot-footer')) {
        menuItems.insertFooter.hide()
        menuItems.removeFooter.show()
      }
      headerFooterFactory.loadElement(el)
    })

    if (!hasBody) {
      $allElmts = $(editor.getBody()).children()
      headerFooterFactory.insertBody()
      var $body = $(headerFooterFactory.body.node)
      $body.empty()
      $allElmts.each(function (i, el) {
        var $el = $(el)
        if (!$el.attr('data-headfoot')) {
          $body.append($el)
        }
      })
    }
  }
}

/**
 * Initialize menu items states (show, hide, ...) and implements onclick handlers
 * @function
 * @inner
 * @param {HeaderFooterFactory} factory The header and footer factory
 * @param {object} menuItems The set of plugin's menu items
 * @returns undefined
 */
function initMenuItems (factory, menuItems) {
  // on startup, hide remove buttons
  menuItems.removeHeader.hide()
  menuItems.removeFooter.hide()

  // override insertHeader, insertFooter, removeHeader and removeFooter onclick handlers
  menuItems.insertHeader.onclick = function () {
    factory.insertHeader()
    menuItems.insertHeader.hide()
    menuItems.removeHeader.show()
  }
  menuItems.insertFooter.onclick = function () {
    factory.insertFooter()
    menuItems.insertFooter.hide()
    menuItems.removeFooter.show()
  }
  menuItems.removeHeader.onclick = function () {
    factory.removeHeader()
    menuItems.insertHeader.show()
    menuItems.removeHeader.hide()
  }
  menuItems.removeFooter.onclick = function () {
    factory.removeFooter()
    menuItems.insertFooter.show()
    menuItems.removeFooter.hide()
  }
}
<commit_msg>make sure the cursor is always onto an allowed section<commit_after>'use strict'

/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 2016 SIRAP Group All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * plugin.js Tinymce plugin headersfooters
 * @file plugin.js
 * @module
 * @name tinycmce-plugin-headersfooters
 * @description
 * A plugin for tinymce WYSIWYG HTML editor that allow to insert headers and footers
 * It will may be used with requires tinymce-plugin-paginate the a near future, but not for now.
 * @link https://github.com/sirap-group/tinymce-plugin-headersfooters
 * @author Rémi Becheras
 * @author Groupe SIRAP
 * @license GNU GPL-v2 http://www.tinymce.com/license
 * @version 1.0.0
 */

/**
 * Tinymce library - injected by the plugin loader.
 * @external tinymce
 * @see {@link https://www.tinymce.com/docs/api/class/tinymce/|Tinymce API Reference}
 */
var tinymce = window.tinymce

/**
 * The jQuery plugin namespace - plugin dependency.
 * @external "jQuery.fn"
 * @see {@link http://learn.jquery.com/plugins/|jQuery Plugins}
 */
var $ = window.jQuery

var ui = require('./utils/ui')
var HeaderFooterFactory = require('./classes/HeaderFooterFactory')

// Add the plugin to the tinymce PluginManager
tinymce.PluginManager.add('headersfooters', tinymcePluginHeadersFooters)

/**
 * Tinymce plugin headers/footers
 * @function
 * @global
 * @param {tinymce.Editor} editor - The injected tinymce editor.
 * @returns void
 */
function tinymcePluginHeadersFooters (editor, url) {
  var headerFooterFactory

  var menuItems = {
    insertHeader: ui.createInsertHeaderMenuItem(),
    insertFooter: ui.createInsertFooterMenuItem(),
    removeHeader: ui.createRemoveHeaderMenuItem(),
    removeFooter: ui.createRemoveFooterMenuItem()
  }

  // add menu items
  editor.addMenuItem('insertHeader', menuItems.insertHeader)
  editor.addMenuItem('removeHeader', menuItems.removeHeader)
  editor.addMenuItem('insertFooter', menuItems.insertFooter)
  editor.addMenuItem('removeFooter', menuItems.removeFooter)

  editor.on('init', onInitHandler)
  editor.on('SetContent', onSetContent)
  editor.on('NodeChange', onNodeChange)

  /**
   * On init event handler. Instanciate the factory and initialize menu items states
   * @function
   * @inner
   * @returns void
   */
  function onInitHandler () {
    headerFooterFactory = new HeaderFooterFactory(editor)
    initMenuItems(headerFooterFactory, menuItems)
  }

  /**
   * On SetContent event handler. Load or reload headers and footers from existing elements if it should do.
   * @function
   * @inner
   * @returns void
   */
  function onSetContent (evt) {
    // var $bodyElmt = $('body', editor.getDoc())
    var content = $(editor.getBody()).html()
    var emptyContent = '

'
    if (content && content !== emptyContent) {
      if (headerFooterFactory) {
        reloadHeadFoots(menuItems)
      } else {
        setTimeout(reloadHeadFoots.bind(null, menuItems), 100)
      }
    } else {
      // evt.content = '
' + evt.content + '
'
    }
  }

  function onNodeChange (evt, b, c) {
    if (headerFooterFactory.hasBody()) {
      var lastParent = evt.parents[evt.parents.length - 1]
      var allowedLocations = [headerFooterFactory.body.node]

      if (headerFooterFactory.hasHeader()) {
        allowedLocations.push(headerFooterFactory.header.node)
      }
      if (headerFooterFactory.hasFooter()) {
        allowedLocations.push(headerFooterFactory.footer.node)
      }

      if (!~allowedLocations.indexOf(lastParent)) {
        headerFooterFactory.focusToEndOfBody()
      }
    }
  }

  /**
   * Helper function. Do the reload of headers and footers
   * @function
   * @inner
   * @returns void
   */
  function reloadHeadFoots (menuItems) {
    var $headFootElmts = $('*[data-headfoot]', editor.getDoc())
    var $bodyElmt = $('*[data-headfoot-body]', editor.getDoc())
    var hasBody = !!$bodyElmt.length
    var $allElmts = null

    // init starting states
    menuItems.insertHeader.show()
    menuItems.insertFooter.show()
    menuItems.removeHeader.hide()
    menuItems.removeFooter.hide()

    // set another state and load elements if a header or a footer exists
    $headFootElmts.each(function (i, el) {
      var $el = $(el)
      if ($el.attr('data-headfoot-header')) {
        menuItems.insertHeader.hide()
        menuItems.removeHeader.show()
      } else if ($el.attr('data-headfoot-body')) {
        // @TODO something ?
      } else if ($el.attr('data-headfoot-footer')) {
        menuItems.insertFooter.hide()
        menuItems.removeFooter.show()
      }
      headerFooterFactory.loadElement(el)
    })

    if (!hasBody) {
      $allElmts = $(editor.getBody()).children()
      headerFooterFactory.insertBody()
      var $body = $(headerFooterFactory.body.node)
      $body.empty()
      $allElmts.each(function (i, el) {
        var $el = $(el)
        if (!$el.attr('data-headfoot')) {
          $body.append($el)
        }
      })
    }
  }
}

/**
 * Initialize menu items states (show, hide, ...) and implements onclick handlers
 * @function
 * @inner
 * @param {HeaderFooterFactory} factory The header and footer factory
 * @param {object} menuItems The set of plugin's menu items
 * @returns undefined
 */
function initMenuItems (factory, menuItems) {
  // on startup, hide remove buttons
  menuItems.removeHeader.hide()
  menuItems.removeFooter.hide()

  // override insertHeader, insertFooter, removeHeader and removeFooter onclick handlers
  menuItems.insertHeader.onclick = function () {
    factory.insertHeader()
    menuItems.insertHeader.hide()
    menuItems.removeHeader.show()
  }
  menuItems.insertFooter.onclick = function () {
    factory.insertFooter()
    menuItems.insertFooter.hide()
    menuItems.removeFooter.show()
  }
  menuItems.removeHeader.onclick = function () {
    factory.removeHeader()
    menuItems.insertHeader.show()
    menuItems.removeHeader.hide()
  }
  menuItems.removeFooter.onclick = function () {
    factory.removeFooter()
    menuItems.insertFooter.show()
    menuItems.removeFooter.hide()
  }
}
<|endoftext|>


<commit_before>export { default as createStore } from "./state/store";
export { Container, mapper } from "./containers/container";
export { Link, Match, enableHistory } from "./router/index";
export { selectorMemoizer } from "./utilities/memoizer";
<commit_msg>bug fix export<commit_after>export { default as createStore } from "./state/store";
export { Container, mapper } from "./containers/container";
export { Link, Match, enableHistory } from "./router/index";
export { defauly as selectorMemoizer } from "./utilities/memoizer";
<|endoftext|>


<commit_before>(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
        window.location.search.slice(1).split("&").some(function (searchTerm) {
          if (searchTerm.indexOf("lang=") === 0) {
            lang = searchTerm.split("=").slice(1).join("=");
            return true;
          }
        });

        return lang.toLowerCase();
      })(window.navigator.userLanguage || window.navigator.language),
      language = languageDialect.split("-")[0],
      Translations = (function() {
        var scope = "_scope";

        function Translations(language, o) {
          this.language = language;
          this[scope] = o;
        }

        Translations.prototype.find = function(path) {
          return path.split(".").reduce(function (obj, key) {
            return obj ? obj[key] : undefined;
          }, this[scope]);
        };

        Translations.prototype.translate = function(ele) {
          if(getLang(ele) === this.language) {
            if(!ele.hasAttribute("data-i18n")) {
              [].slice.call(ele.querySelectorAll("[lang]:not([lang='" + this.language + "'])")).forEach(this.translate, this);
            }
          } else if(ele.hasAttribute("data-i18n")) {
            applyTranslationToElement(ele, this);
          } else {
            [].slice.call(ele.querySelectorAll("[data-i18n]")).map(function(match) {
              if(getLang(match, ele) !== this.language) {
                applyTranslationToElement(match, this);
              }
            }, this);
          }

          return ele;
        };

        return Translations;
      }());

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("data-i18n")) {
      applyTranslation(ele, ele.getAttribute("data-i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
    var translated = obj.find(path);

    if (typeof(translated) === "object" && !Array.isArray(translated)) {
      console.warn("Could not translate %o: path '%s' is of type object", ele, path);
    } else if(typeof(translated) !== "undefined") {
      clean(ele);
      ele.appendChild(toDom(translated));
      ele.lang = language;
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang.toLowerCase();
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function clean(ele) {
    var child;
    while((child = ele.firstChild)) {
      ele.removeChild(child);
    }
  }

  function toDom(content) {
    if(Array.isArray(content)) {
      return content.reduce(function(frag, text) {
        var ele = document.createElement("p");
        ele.textContent = text;
        frag.appendChild(ele);

        return frag;
      }, document.createDocumentFragment());
    }
    return document.createTextNode(content);
  }

  global.i18n = {
    base: (document.documentElement.getAttribute("data-i18n-base") || "locales/"),
    set: (document.documentElement.getAttribute("data-i18n-set") || "translation"),
    loadTranslations: function(lang, set, base) {
      var url = (base || this.base || "") + (lang || language) + "/" + (set || this.set) + ".json";
      console.info("loading translations from: " + url);

      return fetch(url, {
        headers: {
          "Accept": "application/json"
        }
      }).then(function (res) {
        if (res.status >= 200 && res.status < 300) {
          console.info("successfully loaded translations");

          return res.json().then(function(obj) {
            return new Translations(lang || language, obj);
          });
        }

        // TODO add error object instead of false
        return Promise.reject(false);
      }).catch(function (err) {
        console.error("Error loading translations: %o", err);
      });
    },
    translate: function(ele) {
      if(!this.translations) {
        this.translations = this.loadTranslations();
      }

      return this.translations.then(function(translations) {
        return translations.translate(ele);
      });
    },
    translateAll: function() {
      this.translatedAll = true;
      return this.translate(document.documentElement);
    },
    set language(lang) {
      lang = String(lang);

      if(lang !== language) {
        language = lang;

        if(this.translations) {
          this.translations = this.loadTranslations();
        }
        if(this.translatedAll) {
          this.translateAll(lang);
        }
      }
    },
    get language() {
      return language;
    }
  };

  if(!document.documentElement.hasAttribute("data-i18n-disable-auto")) {
    global.i18n.translateAll();
  }
}(this));
<commit_msg>Fix #26<commit_after>(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
        window.location.search.slice(1).split("&").some(function (searchTerm) {
          if (searchTerm.indexOf("lang=") === 0) {
            lang = searchTerm.split("=").slice(1).join("=");
            return true;
          }
        });

        return lang.toLowerCase();
      })(window.navigator.userLanguage || window.navigator.language),
      language = languageDialect.split("-")[0],
      Translations = (function() {
        var scope = "_scope";

        function Translations(language, o) {
          this.language = language;
          this[scope] = o;
        }

        Translations.prototype.find = function(path) {
          return path.split(".").reduce(function (obj, key) {
            return obj ? obj[key] : undefined;
          }, this[scope]);
        };

        Translations.prototype.translate = function(ele) {
          if(getLang(ele) === this.language) {
            if(!ele.hasAttribute("data-i18n")) {
              [].slice.call(ele.querySelectorAll("[lang]:not([lang='" + this.language + "'])")).forEach(this.translate, this);
            }
          } else if(ele.hasAttribute("data-i18n")) {
            applyTranslationToElement(ele, this);
          } else {
            [].slice.call(ele.querySelectorAll("[data-i18n]")).map(function(match) {
              if(getLang(match, ele) !== this.language) {
                applyTranslationToElement(match, this);
              }
            }, this);
          }

          return ele;
        };

        return Translations;
      }()),
      debug = (function() {
        var debug = {
          enabled: false
        };

        ["info", "warn", "error"].forEach(function(name) {
          debug[name] = function() {
            if(this.enabled) {
              console[name].apply(console, arguments);
            }
          };
        });

        return debug;
      }());

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("data-i18n")) {
      applyTranslation(ele, ele.getAttribute("data-i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
    var translated = obj.find(path);

    if (typeof(translated) === "object" && !Array.isArray(translated)) {
      debug.warn("Could not translate %o: path '%s' is of type object", ele, path);
    } else if(typeof(translated) !== "undefined") {
      clean(ele);
      ele.appendChild(toDom(translated));
      ele.lang = language;
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang.toLowerCase();
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function clean(ele) {
    var child;
    while((child = ele.firstChild)) {
      ele.removeChild(child);
    }
  }

  function toDom(content) {
    if(Array.isArray(content)) {
      return content.reduce(function(frag, text) {
        var ele = document.createElement("p");
        ele.textContent = text;
        frag.appendChild(ele);

        return frag;
      }, document.createDocumentFragment());
    }
    return document.createTextNode(content);
  }

  global.i18n = {
    base: (document.documentElement.getAttribute("data-i18n-base") || "locales/"),
    set: (document.documentElement.getAttribute("data-i18n-set") || "translation"),
    loadTranslations: function(lang, set, base) {
      var url;
      base = base || this.base || "";
      lang = lang || language;
      set = set || this.set;
      url = base + lang + "/" + set + ".json";

      debug.info("loading translations for %s from: %s", lang, url);

      return fetch(url, {
        headers: {
          "Accept": "application/json"
        }
      }).then(function (res) {
        if (res.status >= 200 && res.status < 300) {
          debug.info("successfully loaded translations for %s", lang);

          return res.json().then(function(obj) {
            return new Translations(lang || language, obj);
          });
        }

        // TODO add error object instead of false
        return Promise.reject(false);
      }).catch(function (err) {
        debug.error("Error loading translations for %s: %o", lang, err);
        return Promise.reject(err);
      });
    },
    translate: function(ele) {
      if(!this.translations) {
        this.translations = this.loadTranslations();
      }

      return this.translations.then(function(translations) {
        return translations.translate(ele);
      });
    },
    translateAll: function() {
      this.translatedAll = true;
      return this.translate(document.documentElement);
    },
    set language(lang) {
      lang = String(lang);

      if(lang !== language) {
        language = lang;

        if(this.translations) {
          this.translations = this.loadTranslations();
        }
        if(this.translatedAll) {
          this.translateAll(lang);
        }
      }
    },
    get language() {
      return language;
    },

    get debug() {
      return debug.enabled;
    },
    set debug(val) {
      debug.enabled = Boolean(val);
    }
  };

  if(!document.documentElement.hasAttribute("data-i18n-disable-auto")) {
    global.i18n.translateAll();
  }
}(this));
<|endoftext|>


<commit_before>var PIXI = require('pixi.js');
var kd = require('keydrown');
var World = require('./world');

//var canvasHeight = document.body.clientHeight; //this does not work in chrome apparently
var canvasHeight = 900;
var canvasWidth = document.body.clientWidth;
var renderer = new PIXI.CanvasRenderer(canvasWidth, canvasHeight);

document.body.appendChild(renderer.view);

var stage = new PIXI.Stage();

var levels = {
	0: require('../levels/level.json')
};

var wintext = new PIXI.Text("Test change", {
					font: "100px Arial",
					fill: 'white'
				});
var keypressed = false;

var activeWorld = null;
loadWorld();

requestAnimationFrame(animate);

function loadWorld() {
	var world = new World(levels[0]);
	activeWorld = world;
	stage.addChild(world.scene);
}

function animate() {
	kd.tick();

	if(keypressed) {
		stage.addChild(wintext);
	} else {
		stage.removeChild(wintext);
	}

	if (activeWorld) {
		if(activeWorld.monsters) {
			activeWorld.monsters.forEach((monster) => {
				//TODO alter model coordinate depending on direction
				var newCoords = toCanvasCoordinates(monster.modelx, monster.modely);
				monster.x = newCoords[0];
				monster.y = newCoords[1];
				monster.init();
			});
		}

		/*if(activeWorld.walls) {
			activeWorld.walls.forEach((wall) => {
				var newCoords = toCanvasCoordinates(wall.modelx, wall.modely);
				wall.x = newCoords[0];
				wall.y = newCoords[1];
				wall.init();
			});
		}*/
	}

	renderer.render(stage);

	requestAnimationFrame(animate);
}

function toModelCoordinates(x, y) {
	return [x / canvasWidth * activeWorld.modelWidth,
		y / canvasHeight * activeWorld.modelHeight];
}

function toCanvasCoordinates(x, y) {
	return [x / activeWorld.modelWidth * canvasWidth,
		y / activeWorld.modelHeight * canvasHeight];
}

kd.Q.down(() => {
	keypressed = true;
});

kd.W.down(() => {
	keypressed = false;
});

<commit_msg>quickwin!<commit_after>var PIXI = require('pixi.js');
var kd = require('keydrown');
var World = require('./world');

//var canvasHeight = document.body.clientHeight; //this does not work in chrome apparently
var canvasHeight = 900;
var canvasWidth = document.body.clientWidth;
var renderer = new PIXI.CanvasRenderer(canvasWidth, canvasHeight);

document.body.appendChild(renderer.view);

var stage = new PIXI.Stage(0xFFFFFF);

var levels = {
	0: require('../levels/level.json')
};

var wintext = new PIXI.Text("Test change", {
					font: "100px Arial",
					fill: 'white'
				});
var keypressed = false;

var activeWorld = null;
loadWorld();

requestAnimationFrame(animate);

function loadWorld() {
	var world = new World(levels[0]);
	activeWorld = world;
	stage.addChild(world.scene);
}

function animate() {
	kd.tick();

	if(keypressed) {
		stage.addChild(wintext);
	} else {
		stage.removeChild(wintext);
	}

	if (activeWorld) {
		if(activeWorld.monsters) {
			activeWorld.monsters.forEach((monster) => {
				//TODO alter model coordinate depending on direction
				var newCoords = toCanvasCoordinates(monster.modelx, monster.modely);
				monster.x = newCoords[0];
				monster.y = newCoords[1];
				monster.init();
			});
		}

		/*if(activeWorld.walls) {
			activeWorld.walls.forEach((wall) => {
				var newCoords = toCanvasCoordinates(wall.modelx, wall.modely);
				wall.x = newCoords[0];
				wall.y = newCoords[1];
				wall.init();
			});
		}*/
	}

	renderer.render(stage);

	requestAnimationFrame(animate);
}

function toModelCoordinates(x, y) {
	return [x / canvasWidth * activeWorld.modelWidth,
		y / canvasHeight * activeWorld.modelHeight];
}

function toCanvasCoordinates(x, y) {
	return [x / activeWorld.modelWidth * canvasWidth,
		y / activeWorld.modelHeight * canvasHeight];
}

kd.Q.down(() => {
	keypressed = true;
});

kd.W.down(() => {
	keypressed = false;
});

<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import styled from 'styled-components'
import Grid from 'material-ui/Grid'
import Collapse from 'material-ui/transitions/Collapse';
import Drawer from 'material-ui/Drawer';
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import { withTheme } from 'material-ui/styles';
import { SubmissionList, SubmissionDetail, Sidebar, ImageViewer, Header, SubredditDescription } from '../components'
import getTheme from '../themes'
import { observer, inject } from 'mobx-react'
import { compose } from 'recompose'
import { transition } from '../utils/TransitionUtils'

const enhance = compose(
  inject('viewStore'),
  inject('submissionStore'),
  inject('subredditStore'),
  withTheme(),
  observer,
)

const selectedTheme = createMuiTheme(getTheme());
console.log("selectedTheme", selectedTheme)
const TopicListGrid = styled(Grid) `
  // padding-right: 0px !important;
`
const StyledGrid = styled(Grid) `
  transition: ${transition(['padding-left'])};
  ${props => (props.showSidebar ? 'padding-left: 248px' : 'padding-left: 0px')}
`
const _Drawer = styled(Drawer) `

  .paper{
    transition: ${transition(['margin-left'])};
    width: 240px;
    margin-left: ${props => (props.open ? '0px' : '-240px')};
  }
`

export default enhance(function HomePage(props) {
  let { viewStore: { showSidebar } } = props

  return (
    <MuiThemeProvider theme={selectedTheme}>

        <_Drawer
          theme={props.theme}
          type="permanent"
          open={showSidebar}
          classes={{
            paper: 'paper',
          }}
        >
          <Sidebar />
        </_Drawer>

    </MuiThemeProvider>
  )
})
<commit_msg>Fix animation on HamePage<commit_after>// @flow
import React, { Component } from 'react';
import styled from 'styled-components'
import Grid from 'material-ui/Grid'
import Collapse from 'material-ui/transitions/Collapse';
import Drawer from 'material-ui/Drawer';
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import { withTheme } from 'material-ui/styles';
import { SubmissionList, SubmissionDetail, Sidebar, ImageViewer, Header, SubredditDescription } from '../components'
import getTheme from '../themes'
import { observer, inject } from 'mobx-react'
import { compose } from 'recompose'
import { transition } from '../utils/TransitionUtils'

const enhance = compose(
  inject('viewStore'),
  inject('submissionStore'),
  inject('subredditStore'),
  withTheme(),
  observer,
)

const selectedTheme = createMuiTheme(getTheme());
console.log("selectedTheme", selectedTheme)
const TopicListGrid = styled(Grid) `
  // padding-right: 0px !important;
`

const ContentContainer = styled.div`
  display: flex;
  justify-content: center;
  transition: ${transition(['padding-left'])};
  ${props => (props.showSidebar ? 'padding-left: 248px' : 'padding-left: 0px')}
`
const Content = styled.div`
  width: 960px;
`
const StyledGrid = styled(Grid) `
  width: 960px;
  transition: ${transition(['padding-left'])};

`
const _Drawer = styled(Drawer) `

  .paper{
    transition: ${transition(['margin-left'])};
    width: 240px;
    margin-left: ${props => (props.open ? '0px' : '-240px')};
  }
`

export default enhance(function HomePage(props) {
  let { viewStore: { showSidebar } } = props

  return (
    <MuiThemeProvider theme={selectedTheme}>

        <_Drawer
          theme={props.theme}
          type="permanent"
          open={showSidebar}
          classes={{
            paper: 'paper',
          }}
        >
          <Sidebar />
        </_Drawer>
        <ContentContainer theme={props.theme} showSidebar={showSidebar}>
          <Content>

          </Content>
        </ContentContainer>

    </MuiThemeProvider>
  )
})
<|endoftext|>


<commit_before>import Vue from 'vue'
import { sync } from 'vuex-router-sync'

import App from './App'
import i18n from './lib/i18n'
import resizableColumn from './directives/resizable-column'
import router from './router'
import store from './store'

import Autocomplete from 'v-autocomplete'
import Chart from 'chart.js'
import Meta from 'vue-meta'
import VueChartkick from 'vue-chartkick'
import VueCookie from 'vue-cookie'
import VueDragDrop from 'vue-drag-drop'
import VueLazyload from 'vue-lazyload'
import vuescroll from 'vue-scroll'
import VueTextareaAutosize from 'vue-textarea-autosize'
import VTooltip from 'v-tooltip'
import VueWebsocket from 'vue-websocket'

import 'v-autocomplete/dist/v-autocomplete.css'

Vue.use(VueWebsocket, '/events', { transports: ['websocket', 'polling'] })
Vue.config.productionTip = false
Vue.use(Autocomplete)
Vue.use(Meta)
Vue.use(resizableColumn)
Vue.use(VTooltip)
Vue.use(VueChartkick, { adapter: Chart })
Vue.use(VueCookie)
Vue.use(VueLazyload)
Vue.use(vuescroll)
Vue.use(VueDragDrop)
Vue.use(VueTextareaAutosize)

// Make the current route part of the main state.
sync(store, router)

// Global custom directive to enable automatic focus on field after page
// loading.
Vue.directive('focus', {
  inserted (el) {
    el.focus()
  }
})

// Allow access to i18n object from vue instance.
Vue.prototype.$locale = {
  change (locale) {
    i18n.locale = locale
  },
  current () {
    return i18n.locale
  }
}

// Start application.
/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  i18n,
  router,
  store
})

Vue.config.keyCodes.backspace = 8
<commit_msg>[qa] Remove websocket temporarly because of old installations<commit_after>import Vue from 'vue'
import { sync } from 'vuex-router-sync'

import App from './App'
import i18n from './lib/i18n'
import resizableColumn from './directives/resizable-column'
import router from './router'
import store from './store'

import Autocomplete from 'v-autocomplete'
import Chart from 'chart.js'
import Meta from 'vue-meta'
import VueChartkick from 'vue-chartkick'
import VueCookie from 'vue-cookie'
import VueDragDrop from 'vue-drag-drop'
import VueLazyload from 'vue-lazyload'
import vuescroll from 'vue-scroll'
import VueTextareaAutosize from 'vue-textarea-autosize'
import VTooltip from 'v-tooltip'
import VueWebsocket from 'vue-websocket'

import 'v-autocomplete/dist/v-autocomplete.css'

Vue.use(VueWebsocket, '/events')
Vue.config.productionTip = false
Vue.use(Autocomplete)
Vue.use(Meta)
Vue.use(resizableColumn)
Vue.use(VTooltip)
Vue.use(VueChartkick, { adapter: Chart })
Vue.use(VueCookie)
Vue.use(VueLazyload)
Vue.use(vuescroll)
Vue.use(VueDragDrop)
Vue.use(VueTextareaAutosize)

// Make the current route part of the main state.
sync(store, router)

// Global custom directive to enable automatic focus on field after page
// loading.
Vue.directive('focus', {
  inserted (el) {
    el.focus()
  }
})

// Allow access to i18n object from vue instance.
Vue.prototype.$locale = {
  change (locale) {
    i18n.locale = locale
  },
  current () {
    return i18n.locale
  }
}

// Start application.
/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  template: '<App/>',
  i18n,
  router,
  store
})

Vue.config.keyCodes.backspace = 8
<|endoftext|>


<commit_before>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { VueRouter, router } from './router'
import App from './App'

Vue.use(VueRouter)

/* eslint-disable no-new */
new Vue({
    router,
    el: '#app',
    template: '<App/>',
    components: { App }
})
<commit_msg>Import styles<commit_after>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { VueRouter, router } from './router'
import App from './App'

import './styles.js'

/* eslint-disable no-new */
new Vue({
    router,
    el: '#app',
    template: '<App/>',
    components: { App }
})
<|endoftext|>


<commit_before>var http = require('http');
var CronJob = require('cron').CronJob;

var currentWeek = 'UNKNOWN';

module.exports.currentWeek = function() {
  if (currentWeek != 'UNKNOWN') {
    return currentWeek;
  } else {
    var week = '';
    var schedulePageOptions = {
      host: 'www.portergaud.edu',
      port: 80,
      path: '/page.cfm?p=1430',
      method: 'GET'
    };

    var req = http.request(schedulePageOptions, function(res) {
      var data = '';
      res.on('data', function(d) {
        data += d;
      });
      res.on('end', function() {
        if (data.indexOf('A Week') > -1 || data.indexOf('Week A') > -1) {
          week = 'A';
        } else if (data.indexOf('B Week') > -1 || data.indexOf('Week B') > -1) {
          week = 'B';
        } else {
          week = 'WEEKEND';
        }
        currentWeek = week;
        return week;
      });
    });
    req.end();
  }
};

module.exports.currentWeek();

new CronJob('00 01 00 * * *', function() {
  console.log('Automatically updating the week...');
  currentWeek = 'UNKNOWN';
  module.exports.currentWeek();
}, null, true, 'America/New_York');

module.exports.getFutureWeek = function(day) {
  if (day.getDay() === 0 || day.getDay() === 6 || day.getFullYear() == '2017') {
    return ''; // will be implemented closer to 2017
  }
  return (day.getWeek() % 2 === 0) ? 'A' : 'B';
  // var week = '';
  // console.log('http://www.portergaud.edu/page.cfm?p=1346&start=' + month + '/' + date + '/' + year + '&period=week');
  // var request = http.get('http://www.portergaud.edu/page.cfm?p=1346&start=' + month + '/' + date + '/' + year + '&period=week', function(response) {
  //   var data = '';
  //   response.on('end', function(d) {
  //     data += d;
  //   });
  //   response.on('end', function() {
  //     if (data.indexOf('A Week' > -1) || data.indexOf('Week A' > -1)) {
  //       week = 'A';
  //     } else if (data.indexOf('B Week' > -1) || data.indexOf('Week B' > -1)) {
  //       week = 'B';
  //     } else {
  //       week = 'WEEKEND';
  //     }
  //     console.log(week);
  //     return week;
  //   });
  // });
};

Date.prototype.getWeek = function() {
  var d = new Date(+this);
  d.setHours(0, 0, 0);
  d.setDate(d.getDate() + 4 - (d.getDay() || 7));
  return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7) + 1) / 7);
};
<commit_msg>Use calendar page for grabbing the week<commit_after>var http = require('http');
var CronJob = require('cron').CronJob;

var currentWeek = 'UNKNOWN';

module.exports.currentWeek = function() {
  if (currentWeek != 'UNKNOWN') {
    return currentWeek;
  } else {
    var week = '';
    var schedulePageOptions = {
      host: 'www.portergaud.edu',
      port: 80,
      path: '/page.cfm?p=1346&period=week',
      method: 'GET'
    };

    var req = http.request(schedulePageOptions, function(res) {
      var data = '';
      res.on('data', function(d) {
        data += d;
      });
      res.on('end', function() {
        if (data.indexOf('A Week') > -1 || data.indexOf('Week A') > -1) {
          week = 'A';
        } else if (data.indexOf('B Week') > -1 || data.indexOf('Week B') > -1) {
          week = 'B';
        } else {
          week = 'WEEKEND';
        }
        currentWeek = week;
        return week;
      });
    });
    req.end();
  }
};

module.exports.currentWeek();

new CronJob('00 01 00 * * *', function() {
  console.log('Automatically updating the week...');
  currentWeek = 'UNKNOWN';
  module.exports.currentWeek();
}, null, true, 'America/New_York');

module.exports.getFutureWeek = function(day) {
  if (day.getDay() === 0 || day.getDay() === 6 || day.getFullYear() == '2017') {
    return ''; // will be implemented closer to 2017
  }
  return (day.getWeek() % 2 === 0) ? 'A' : 'B';
  // var week = '';
  // console.log('http://www.portergaud.edu/page.cfm?p=1346&start=' + month + '/' + date + '/' + year + '&period=week');
  // var request = http.get('http://www.portergaud.edu/page.cfm?p=1346&start=' + month + '/' + date + '/' + year + '&period=week', function(response) {
  //   var data = '';
  //   response.on('end', function(d) {
  //     data += d;
  //   });
  //   response.on('end', function() {
  //     if (data.indexOf('A Week' > -1) || data.indexOf('Week A' > -1)) {
  //       week = 'A';
  //     } else if (data.indexOf('B Week' > -1) || data.indexOf('Week B' > -1)) {
  //       week = 'B';
  //     } else {
  //       week = 'WEEKEND';
  //     }
  //     console.log(week);
  //     return week;
  //   });
  // });
};

Date.prototype.getWeek = function() {
  var d = new Date(+this);
  d.setHours(0, 0, 0);
  d.setDate(d.getDate() + 4 - (d.getDay() || 7));
  return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7) + 1) / 7);
};
<|endoftext|>


<commit_before>

var ship;

function create() {

    game.physics.startSystem(Phaser.Physics.ARCADE);

    //Ship creation
    var ship_polygon = new Phaser.Polygon([
        new Phaser.Point(0, 0),
        new Phaser.Point(32, 16),
        new Phaser.Point(0, 32),
        new Phaser.Point(10, 16),
        new Phaser.Point(0, 0)
    ]);

    var graphics = game.add.graphics(0, 0);
    graphics.beginFill(0x6633BB);
    graphics.drawPolygon(ship_polygon.points);

    ship = game.add.sprite(400, 300);
    ship.addChild(graphics);

    game.physics.arcade.enable(ship, Phaser.Physics.ARCADE);
    ship.body.velocity.x = 20;
}

function update() {
    var keyboard = game.input.keyboard;
    var max_velocity = 250;

    if (keyboard.isDown(Phaser.Keyboard.LEFT)) {
        ship.body.velocity.x -= 5;
    }
    if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {
        ship.body.velocity.x += 5;
    }
    if (keyboard.isDown(Phaser.Keyboard.UP)) {
        ship.body.velocity.y -= 5;
    }
    if (keyboard.isDown(Phaser.Keyboard.DOWN)) {
        ship.body.velocity.y += 5;
    }

    if (ship.body.velocity.x < - max_velocity)
        ship.body.velocity.x = - max_velocity;
    if (ship.body.velocity.x > max_velocity)
        ship.body.velocity.x = max_velocity;
    if (ship.body.velocity.y < - max_velocity)
        ship.body.velocity.y = - max_velocity;
    if (ship.body.velocity.y > max_velocity)
        ship.body.velocity.y = max_velocity;
}

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'Bubble screen', { create: create, update: update });
<commit_msg>Without user inpout, the ship slow down and stop.<commit_after>

var ship;

function create() {

    game.physics.startSystem(Phaser.Physics.ARCADE);

    //Ship creation
    var ship_polygon = new Phaser.Polygon([
        new Phaser.Point(0, 0),
        new Phaser.Point(32, 16),
        new Phaser.Point(0, 32),
        new Phaser.Point(10, 16),
        new Phaser.Point(0, 0)
    ]);

    var graphics = game.add.graphics(0, 0);
    graphics.beginFill(0x6633BB);
    graphics.drawPolygon(ship_polygon.points);

    ship = game.add.sprite(400, 300);
    ship.addChild(graphics);

    game.physics.arcade.enable(ship, Phaser.Physics.ARCADE);
}

function update() {
    var keyboard = game.input.keyboard;
    var velocity = ship.body.velocity;
    var max_velocity = 250;

    //The ship always slow down a little.
    if (velocity.x < 2 && velocity.x > -2)
        velocity.x = 0;
    else
        velocity.x += (velocity.x > 0 ? -2 : 2);
    if (velocity.y < 2 && velocity.y > -2)
        velocity.y = 0;
    else
        velocity.y += (velocity.y > 0 ? -2 : 2);

    //Keyboard input
    if (keyboard.isDown(Phaser.Keyboard.LEFT)) {
        ship.body.velocity.x -= 7;
    }
    if (keyboard.isDown(Phaser.Keyboard.RIGHT)) {
        ship.body.velocity.x += 7;
    }
    if (keyboard.isDown(Phaser.Keyboard.UP)) {
        ship.body.velocity.y -= 7;
    }
    if (keyboard.isDown(Phaser.Keyboard.DOWN)) {
        ship.body.velocity.y += 7;
    }

    //Max velocity
    if (ship.body.velocity.x < - max_velocity)
        ship.body.velocity.x = - max_velocity;
    if (ship.body.velocity.x > max_velocity)
        ship.body.velocity.x = max_velocity;
    if (ship.body.velocity.y < - max_velocity)
        ship.body.velocity.y = - max_velocity;
    if (ship.body.velocity.y > max_velocity)
        ship.body.velocity.y = max_velocity;
}

var game = new Phaser.Game(800, 600, Phaser.AUTO, 'Bubble screen', { create: create, update: update });
<|endoftext|>