<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

var THREE = THREE || { REVISION: '52dev' };

if ( self.console === undefined ) {

	self.console = {

		info: function () {},
		log: function () {},
		debug: function () {},
		warn: function () {},
		error: function () {}

	};

}

if ( self.Int32Array === undefined ) {

	self.Int32Array = Array;
	self.Float32Array = Array;

}

// Shims for "startsWith", "endsWith", and "trim" for browsers where this is not yet implemented
// not sure we should have this, or at least not have it here

// http://stackoverflow.com/questions/646628/javascript-startswith
// http://stackoverflow.com/questions/498970/how-do-i-trim-a-string-in-javascript
// http://wiki.ecmascript.org/doku.php?id=harmony%3astring_extras

if ( String.prototype.startsWith === undefined ) {

	String.prototype.startsWith = function ( str ) {

		return this.slice( 0, str.length ) === str;

	};

}

if ( String.prototype.endsWith === undefined ) {

	String.prototype.endsWith = function ( str ) {

		var t = String( str );
		var index = this.lastIndexOf( t );
		return index >= 0 && index === this.length - t.length;

	};

}

if ( ! String.prototype.trim === undefined ) {

	String.prototype.trim = function () {

		return this.replace( /^\s+|\s+$/g, '' );

	};

}

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel

( function () {

	var lastTime = 0;
	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

	for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {

		window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
		window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];

	}

	if ( window.requestAnimationFrame === undefined ) {

		window.requestAnimationFrame = function ( callback, element ) {

			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
			var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
			lastTime = currTime + timeToCall;
			return id;

		};

	}

	if ( window.cancelAnimationFrame === undefined ) {

		window.cancelAnimationFrame = function ( id ) { clearTimeout( id ); };

	}

}() );

// MATERIAL CONSTANTS

// side

THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;

// shading

THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;

// colors

THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;

// blending modes

THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;

// custom blending equations
// (numbers start from 100 not to clash with other
//  mappings to OpenGL constants defined in Texture.js)

THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;

// custom blending destination factors

THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;

// custom blending source factors

//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;

// TEXTURE CONSTANTS

THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;

// Mapping modes

THREE.UVMapping = function () {};

THREE.CubeReflectionMapping = function () {};
THREE.CubeRefractionMapping = function () {};

THREE.SphericalReflectionMapping = function () {};
THREE.SphericalRefractionMapping = function () {};

// Wrapping modes

THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;

// Filters

THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;

// Data types

THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;

// Pixel types

//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;

// Pixel formats

THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;

// Compressed texture formats

THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;

/*
// Potential future PVRTC compressed texture formats
THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
*/
<commit_msg>Update src/Three.js<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 * @modified by Larry Battle / http://bateru.com/news
 */

var THREE = THREE || { REVISION: '52dev' };

self.console = self.console || {

	info: function () {},
	log: function () {},
	debug: function () {},
	warn: function () {},
	error: function () {}

};

if ( self.Int32Array === undefined ) {

	self.Int32Array = Array;
	self.Float32Array = Array;

}

// Shims for "startsWith", "endsWith", and "trim" for browsers where this is not yet implemented
// not sure we should have this, or at least not have it here

// http://stackoverflow.com/questions/646628/javascript-startswith
// http://stackoverflow.com/questions/498970/how-do-i-trim-a-string-in-javascript
// http://wiki.ecmascript.org/doku.php?id=harmony%3astring_extras

String.prototype.startsWith = String.prototype.startsWith || function ( str ) {

	return this.slice( 0, str.length ) === str;

};

String.prototype.endsWith = String.prototype.endsWith || function ( str ) {

	var t = String( str );
	var index = this.lastIndexOf( t );
	return ( -1 < index && index ) === (this.length - t.length);

};

String.prototype.trim = String.prototype.trim || function () {

	return this.replace( /^\s+|\s+$/g, '' );

};

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel

( function () {

	var lastTime = 0;
	var vendors = [ 'ms', 'moz', 'webkit', 'o' ];

	for ( var x = 0; x < vendors.length && !window.requestAnimationFrame; ++ x ) {

		window.requestAnimationFrame = window[ vendors[ x ] + 'RequestAnimationFrame' ];
		window.cancelAnimationFrame = window[ vendors[ x ] + 'CancelAnimationFrame' ] || window[ vendors[ x ] + 'CancelRequestAnimationFrame' ];

	}

	if ( window.requestAnimationFrame === undefined ) {

		window.requestAnimationFrame = function ( callback, element ) {

			var currTime = Date.now(), timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
			var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall );
			lastTime = currTime + timeToCall;
			return id;

		};

	}

	window.cancelAnimationFrame = window.cancelAnimationFrame || function ( id ) { window.clearTimeout( id ); };

}() );

// MATERIAL CONSTANTS

// side

THREE.FrontSide = 0;
THREE.BackSide = 1;
THREE.DoubleSide = 2;

// shading

THREE.NoShading = 0;
THREE.FlatShading = 1;
THREE.SmoothShading = 2;

// colors

THREE.NoColors = 0;
THREE.FaceColors = 1;
THREE.VertexColors = 2;

// blending modes

THREE.NoBlending = 0;
THREE.NormalBlending = 1;
THREE.AdditiveBlending = 2;
THREE.SubtractiveBlending = 3;
THREE.MultiplyBlending = 4;
THREE.CustomBlending = 5;

// custom blending equations
// (numbers start from 100 not to clash with other
//  mappings to OpenGL constants defined in Texture.js)

THREE.AddEquation = 100;
THREE.SubtractEquation = 101;
THREE.ReverseSubtractEquation = 102;

// custom blending destination factors

THREE.ZeroFactor = 200;
THREE.OneFactor = 201;
THREE.SrcColorFactor = 202;
THREE.OneMinusSrcColorFactor = 203;
THREE.SrcAlphaFactor = 204;
THREE.OneMinusSrcAlphaFactor = 205;
THREE.DstAlphaFactor = 206;
THREE.OneMinusDstAlphaFactor = 207;

// custom blending source factors

//THREE.ZeroFactor = 200;
//THREE.OneFactor = 201;
//THREE.SrcAlphaFactor = 204;
//THREE.OneMinusSrcAlphaFactor = 205;
//THREE.DstAlphaFactor = 206;
//THREE.OneMinusDstAlphaFactor = 207;
THREE.DstColorFactor = 208;
THREE.OneMinusDstColorFactor = 209;
THREE.SrcAlphaSaturateFactor = 210;

// TEXTURE CONSTANTS

THREE.MultiplyOperation = 0;
THREE.MixOperation = 1;

// Mapping modes

THREE.UVMapping = function () {};

THREE.CubeReflectionMapping = function () {};
THREE.CubeRefractionMapping = function () {};

THREE.SphericalReflectionMapping = function () {};
THREE.SphericalRefractionMapping = function () {};

// Wrapping modes

THREE.RepeatWrapping = 1000;
THREE.ClampToEdgeWrapping = 1001;
THREE.MirroredRepeatWrapping = 1002;

// Filters

THREE.NearestFilter = 1003;
THREE.NearestMipMapNearestFilter = 1004;
THREE.NearestMipMapLinearFilter = 1005;
THREE.LinearFilter = 1006;
THREE.LinearMipMapNearestFilter = 1007;
THREE.LinearMipMapLinearFilter = 1008;

// Data types

THREE.UnsignedByteType = 1009;
THREE.ByteType = 1010;
THREE.ShortType = 1011;
THREE.UnsignedShortType = 1012;
THREE.IntType = 1013;
THREE.UnsignedIntType = 1014;
THREE.FloatType = 1015;

// Pixel types

//THREE.UnsignedByteType = 1009;
THREE.UnsignedShort4444Type = 1016;
THREE.UnsignedShort5551Type = 1017;
THREE.UnsignedShort565Type = 1018;

// Pixel formats

THREE.AlphaFormat = 1019;
THREE.RGBFormat = 1020;
THREE.RGBAFormat = 1021;
THREE.LuminanceFormat = 1022;
THREE.LuminanceAlphaFormat = 1023;

// Compressed texture formats

THREE.RGB_S3TC_DXT1_Format = 2001;
THREE.RGBA_S3TC_DXT1_Format = 2002;
THREE.RGBA_S3TC_DXT3_Format = 2003;
THREE.RGBA_S3TC_DXT5_Format = 2004;

/*
// Potential future PVRTC compressed texture formats
THREE.RGB_PVRTC_4BPPV1_Format = 2100;
THREE.RGB_PVRTC_2BPPV1_Format = 2101;
THREE.RGBA_PVRTC_4BPPV1_Format = 2102;
THREE.RGBA_PVRTC_2BPPV1_Format = 2103;
*/
<|endoftext|>


<commit_before>import React from "react";
import styled from "@emotion/styled";
import { Link } from "react-router-dom";

const Container = styled.div`
  flex: 9;
  display: flex;
  border: 1rem solid white;
  border-radius: 1rem;
  background-color: white;
  margin: 1%;
  flex-direction: column;
`;

const WordContainer = styled.div`
  margin-top: 2%;
  display: flex;
  width: 100%;
  justify-content: space-evenly;
  flex-wrap: wrap;
`;

const Word = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 3%;
  width: 23%;
  min-height: 18%;
  margin-right: 5%;
  margin-left: 5%;
  background-color: white;
  border-radius: 4rem;
  box-shadow: rgba(255, 153, 0, 0.9) 0px 1px 8px;
  font-size: 3.5rem;
  flex-wrap: wrap;
`;

class MyFoldersContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isDataFetched: false,
      words: [],
      error: false,
      errorMessage: "",
    };
  }

  async componentDidMount() {
    let searchParams = new URLSearchParams();
    searchParams.append("queryWord", this.props.word);
    searchParams.append("numOfWordsRequested", this.props.numWords);
    try {
      const response = await fetch(
        ("/recommendation?" + searchParams.toString())
      ).then((res) => res.json());
      if ("error" in response) {
        this.setState({ error: true, errorMessage: response.error });
      }
      this.setState({ isDataFetched: true, words: response[this.props.word] });
    } catch (err) {
      alert("Could not load, refresh page");
    }
  }

  render() {
    if (!this.state.isDataFetched) {
      return <Container></Container>;
    }
    if (this.state.error) {
      return (
        <Container>

{this.state.errorMessage}

        </Container>
      );
    }
    return (
      <Container>
        <WordContainer>
          {this.state.words.map((word) => {
            return (
              <Word key={word}>
                <Link
                  to={`/CreateCard?word=${word}`}
                  style={{ textDecoration: "none", color: "black" }}>
                  word
                </Link>
              </Word>
            );
          })}
        </WordContainer>
      </Container>
    );
  }
}

export default MyFoldersContent;<commit_msg>Fix small bug inside similar words page<commit_after>import React from "react";
import styled from "@emotion/styled";
import { Link } from "react-router-dom";

const Container = styled.div`
  flex: 9;
  display: flex;
  border: 1rem solid white;
  border-radius: 1rem;
  background-color: white;
  margin: 1%;
  flex-direction: column;
`;

const WordContainer = styled.div`
  margin-top: 2%;
  display: flex;
  width: 100%;
  justify-content: space-evenly;
  flex-wrap: wrap;
`;

const Word = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 3%;
  width: 23%;
  min-height: 18%;
  margin-right: 5%;
  margin-left: 5%;
  background-color: white;
  border-radius: 4rem;
  box-shadow: rgba(255, 153, 0, 0.9) 0px 1px 8px;
  font-size: 3.5rem;
  flex-wrap: wrap;
`;

class MyFoldersContent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isDataFetched: false,
      words: [],
      error: false,
      errorMessage: "",
    };
  }

  async componentDidMount() {
    let searchParams = new URLSearchParams();
    searchParams.append("queryWord", this.props.word);
    searchParams.append("numOfWordsRequested", this.props.numWords);
    try {
      const response = await fetch(
        ("/recommendation?" + searchParams.toString())
      ).then((res) => res.json());
      if ("error" in response) {
        this.setState({ error: true, errorMessage: response.error });
      }
      this.setState({ isDataFetched: true, words: response[this.props.word] });
    } catch (err) {
      alert("Could not load, refresh page");
    }
  }

  render() {
    if (!this.state.isDataFetched) {
      return <Container></Container>;
    }
    if (this.state.error) {
      return (
        <Container>

{this.state.errorMessage}

        </Container>
      );
    }
    return (
      <Container>
        <WordContainer>
          {this.state.words.map((word) => {
            return (
              <Word key={word}>
                <Link
                  to={`/CreateCard?word=${word}`}
                  style={{ textDecoration: "none", color: "black" }}>
                  {word}
                </Link>
              </Word>
            );
          })}
        </WordContainer>
      </Container>
    );
  }
}

export default MyFoldersContent;<|endoftext|>


<commit_before>import Component from '@ember/component';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {action} from '@ember/object';
import {alias, or} from '@ember/object/computed';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';

export default Component.extend({
    feature: service(),
    store: service(),
    config: service(),
    ajax: service(),
    ghostPaths: service(),
    notifications: service(),
    slugGenerator: service(),
    session: service(),
    settings: service(),
    ui: service(),

    tagName: '',

    post: null,

    isViewingSubview: false,

    canonicalUrlScratch: alias('post.canonicalUrlScratch'),
    customExcerptScratch: alias('post.customExcerptScratch'),
    codeinjectionFootScratch: alias('post.codeinjectionFootScratch'),
    codeinjectionHeadScratch: alias('post.codeinjectionHeadScratch'),
    metaDescriptionScratch: alias('post.metaDescriptionScratch'),
    metaTitleScratch: alias('post.metaTitleScratch'),
    ogDescriptionScratch: alias('post.ogDescriptionScratch'),
    ogTitleScratch: alias('post.ogTitleScratch'),
    twitterDescriptionScratch: alias('post.twitterDescriptionScratch'),
    twitterTitleScratch: alias('post.twitterTitleScratch'),
    slugValue: boundOneWay('post.slug'),

    seoDescription: or('metaDescriptionScratch', 'customExcerptScratch', 'post.excerpt'),
    facebookDescription: or('ogDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
    facebookImage: or('post.ogImage', 'post.featureImage', 'settings.ogImage', 'settings.coverImage'),
    facebookTitle: or('ogTitleScratch', 'seoTitle'),
    twitterDescription: or('twitterDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
    twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'),
    twitterTitle: or('twitterTitleScratch', 'seoTitle'),

    showVisibilityInput: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
    showEmailNewsletter: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),

    seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () {
        return this.metaTitleScratch || this.post.titleScratch || '(Untitled)';
    }),

    seoURL: computed('post.{slug,canonicalUrl}', 'config.blogUrl', function () {
        const urlParts = [];

        if (this.post.canonicalUrl) {
            try {
                const canonicalUrl = new URL(this.post.canonicalUrl);
                urlParts.push(canonicalUrl.host);
                urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p));
            } catch (e) {
                // no-op, invalid URL
            }
        } else {
            const blogUrl = new URL(this.config.get('blogUrl'));
            urlParts.push(blogUrl.host);
            urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
            urlParts.push(this.post.slug);
        }

        return urlParts.join(' > ');
    }),

    willDestroyElement() {
        this._super(...arguments);

        let post = this.post;
        let errors = post.get('errors');

        // reset the publish date if it has an error
        if (errors.has('publishedAtBlogDate') || errors.has('publishedAtBlogTime')) {
            post.set('publishedAtBlogTZ', post.get('publishedAtUTC'));
            post.validate({attribute: 'publishedAtBlog'});
        }

        this.setSidebarWidthVariable(0);
    },

    actions: {
        showSubview(subview) {
            this.set('isViewingSubview', true);
            this.set('subview', subview);
        },

        closeSubview() {
            this.set('isViewingSubview', false);
            this.set('subview', null);
        },

        discardEnter() {
            return false;
        },

        toggleFeatured() {
            this.toggleProperty('post.featured');

            // If this is a new post.  Don't save the post.  Defer the save
            // to the user pressing the save button
            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        /**
         * triggered by user manually changing slug
         */
        updateSlug(newSlug) {
            return this.updateSlugTask
                .perform(newSlug)
                .catch((error) => {
                    this.showError(error);
                    this.post.rollbackAttributes();
                });
        },

        setPublishedAtBlogDate(date) {
            let post = this.post;
            let dateString = moment(date).format('YYYY-MM-DD');

            post.get('errors').remove('publishedAtBlogDate');

            if (post.get('isNew') || date === post.get('publishedAtBlogDate')) {
                post.validate({property: 'publishedAtBlog'});
            } else {
                post.set('publishedAtBlogDate', dateString);
                return this.savePostTask.perform();
            }
        },

        async setVisibility(segment) {
            this.post.set('visibilityFilter', segment);
            try {
                await this.post.validate({property: 'visibility'});
                await this.post.validate({property: 'visibilityFilter'});
                if (this.post.changedAttributes().visibilityFilter) {
                    await this.savePostTask.perform();
                }
            } catch (e) {
                if (!e) {
                    // validation error
                    return;
                }

                throw e;
            }
        },

        setPublishedAtBlogTime(time) {
            let post = this.post;

            post.get('errors').remove('publishedAtBlogDate');

            if (post.get('isNew') || time === post.get('publishedAtBlogTime')) {
                post.validate({property: 'publishedAtBlog'});
            } else {
                post.set('publishedAtBlogTime', time);
                return this.savePostTask.perform();
            }
        },

        setCustomExcerpt(excerpt) {
            let post = this.post;
            let currentExcerpt = post.get('customExcerpt');

            if (excerpt === currentExcerpt) {
                return;
            }

            post.set('customExcerpt', excerpt);

            return post.validate({property: 'customExcerpt'}).then(() => this.savePostTask.perform());
        },

        setHeaderInjection(code) {
            let post = this.post;
            let currentCode = post.get('codeinjectionHead');

            if (code === currentCode) {
                return;
            }

            post.set('codeinjectionHead', code);

            return post.validate({property: 'codeinjectionHead'}).then(() => this.savePostTask.perform());
        },

        setFooterInjection(code) {
            let post = this.post;
            let currentCode = post.get('codeinjectionFoot');

            if (code === currentCode) {
                return;
            }

            post.set('codeinjectionFoot', code);

            return post.validate({property: 'codeinjectionFoot'}).then(() => this.savePostTask.perform());
        },

        setMetaTitle(metaTitle) {
            // Grab the post and current stored meta title
            let post = this.post;
            let currentTitle = post.get('metaTitle');

            // If the title entered matches the stored meta title, do nothing
            if (currentTitle === metaTitle) {
                return;
            }

            // If the title entered is different, set it as the new meta title
            post.set('metaTitle', metaTitle);

            // Make sure the meta title is valid and if so, save it into the post
            return post.validate({property: 'metaTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setMetaDescription(metaDescription) {
            // Grab the post and current stored meta description
            let post = this.post;
            let currentDescription = post.get('metaDescription');

            // If the title entered matches the stored meta title, do nothing
            if (currentDescription === metaDescription) {
                return;
            }

            // If the title entered is different, set it as the new meta title
            post.set('metaDescription', metaDescription);

            // Make sure the meta title is valid and if so, save it into the post
            return post.validate({property: 'metaDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setCanonicalUrl(value) {
            // Grab the post and current stored meta description
            let post = this.post;
            let currentCanonicalUrl = post.canonicalUrl;

            // If the value entered matches the stored value, do nothing
            if (currentCanonicalUrl === value) {
                return;
            }

            // If the value supplied is different, set it as the new value
            post.set('canonicalUrl', value);

            // Make sure the value is valid and if so, save it into the post
            return post.validate({property: 'canonicalUrl'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setOgTitle(ogTitle) {
            // Grab the post and current stored facebook title
            let post = this.post;
            let currentTitle = post.get('ogTitle');

            // If the title entered matches the stored facebook title, do nothing
            if (currentTitle === ogTitle) {
                return;
            }

            // If the title entered is different, set it as the new facebook title
            post.set('ogTitle', ogTitle);

            // Make sure the facebook title is valid and if so, save it into the post
            return post.validate({property: 'ogTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setOgDescription(ogDescription) {
            // Grab the post and current stored facebook description
            let post = this.post;
            let currentDescription = post.get('ogDescription');

            // If the title entered matches the stored facebook description, do nothing
            if (currentDescription === ogDescription) {
                return;
            }

            // If the description entered is different, set it as the new facebook description
            post.set('ogDescription', ogDescription);

            // Make sure the facebook description is valid and if so, save it into the post
            return post.validate({property: 'ogDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setTwitterTitle(twitterTitle) {
            // Grab the post and current stored twitter title
            let post = this.post;
            let currentTitle = post.get('twitterTitle');

            // If the title entered matches the stored twitter title, do nothing
            if (currentTitle === twitterTitle) {
                return;
            }

            // If the title entered is different, set it as the new twitter title
            post.set('twitterTitle', twitterTitle);

            // Make sure the twitter title is valid and if so, save it into the post
            return post.validate({property: 'twitterTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setTwitterDescription(twitterDescription) {
            // Grab the post and current stored twitter description
            let post = this.post;
            let currentDescription = post.get('twitterDescription');

            // If the description entered matches the stored twitter description, do nothing
            if (currentDescription === twitterDescription) {
                return;
            }

            // If the description entered is different, set it as the new twitter description
            post.set('twitterDescription', twitterDescription);

            // Make sure the twitter description is valid and if so, save it into the post
            return post.validate({property: 'twitterDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setCoverImage(image) {
            this.set('post.featureImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearCoverImage() {
            this.set('post.featureImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        setOgImage(image) {
            this.set('post.ogImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearOgImage() {
            this.set('post.ogImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        setTwitterImage(image) {
            this.set('post.twitterImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearTwitterImage() {
            this.set('post.twitterImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        changeAuthors(newAuthors) {
            let post = this.post;

            // return if nothing changed
            if (newAuthors.mapBy('id').join() === post.get('authors').mapBy('id').join()) {
                return;
            }

            post.set('authors', newAuthors);
            post.validate({property: 'authors'});

            // if this is a new post (never been saved before), don't try to save it
            if (post.get('isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                post.rollbackAttributes();
            });
        },

        deletePost() {
            if (this.deletePost) {
                this.deletePost();
            }
        }
    },

    showError(error) {
        // TODO: remove null check once ValidationEngine has been removed
        if (error) {
            this.notifications.showAPIError(error);
        }
    },

    setSidebarWidthFromElement: action(function (element) {
        const width = element.getBoundingClientRect().width;
        this.setSidebarWidthVariable(width);
    }),

    setSidebarWidthVariable(width) {
        document.documentElement.style.setProperty('--editor-sidebar-width', `${width}px`);
    }
});
<commit_msg>Fixed chevron character in meta-data preview in post settings<commit_after>import Component from '@ember/component';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {action} from '@ember/object';
import {alias, or} from '@ember/object/computed';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';

export default Component.extend({
    feature: service(),
    store: service(),
    config: service(),
    ajax: service(),
    ghostPaths: service(),
    notifications: service(),
    slugGenerator: service(),
    session: service(),
    settings: service(),
    ui: service(),

    tagName: '',

    post: null,

    isViewingSubview: false,

    canonicalUrlScratch: alias('post.canonicalUrlScratch'),
    customExcerptScratch: alias('post.customExcerptScratch'),
    codeinjectionFootScratch: alias('post.codeinjectionFootScratch'),
    codeinjectionHeadScratch: alias('post.codeinjectionHeadScratch'),
    metaDescriptionScratch: alias('post.metaDescriptionScratch'),
    metaTitleScratch: alias('post.metaTitleScratch'),
    ogDescriptionScratch: alias('post.ogDescriptionScratch'),
    ogTitleScratch: alias('post.ogTitleScratch'),
    twitterDescriptionScratch: alias('post.twitterDescriptionScratch'),
    twitterTitleScratch: alias('post.twitterTitleScratch'),
    slugValue: boundOneWay('post.slug'),

    seoDescription: or('metaDescriptionScratch', 'customExcerptScratch', 'post.excerpt'),
    facebookDescription: or('ogDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
    facebookImage: or('post.ogImage', 'post.featureImage', 'settings.ogImage', 'settings.coverImage'),
    facebookTitle: or('ogTitleScratch', 'seoTitle'),
    twitterDescription: or('twitterDescriptionScratch', 'customExcerptScratch', 'seoDescription', 'post.excerpt', 'settings.description', ''),
    twitterImage: or('post.twitterImage', 'post.featureImage', 'settings.twitterImage', 'settings.coverImage'),
    twitterTitle: or('twitterTitleScratch', 'seoTitle'),

    showVisibilityInput: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),
    showEmailNewsletter: or('session.user.isOwnerOnly', 'session.user.isAdminOnly', 'session.user.isEditor'),

    seoTitle: computed('metaTitleScratch', 'post.titleScratch', function () {
        return this.metaTitleScratch || this.post.titleScratch || '(Untitled)';
    }),

    seoURL: computed('post.{slug,canonicalUrl}', 'config.blogUrl', function () {
        const urlParts = [];

        if (this.post.canonicalUrl) {
            try {
                const canonicalUrl = new URL(this.post.canonicalUrl);
                urlParts.push(canonicalUrl.host);
                urlParts.push(...canonicalUrl.pathname.split('/').reject(p => !p));
            } catch (e) {
                // no-op, invalid URL
            }
        } else {
            const blogUrl = new URL(this.config.get('blogUrl'));
            urlParts.push(blogUrl.host);
            urlParts.push(...blogUrl.pathname.split('/').reject(p => !p));
            urlParts.push(this.post.slug);
        }

        return urlParts.join(' › ');
    }),

    willDestroyElement() {
        this._super(...arguments);

        let post = this.post;
        let errors = post.get('errors');

        // reset the publish date if it has an error
        if (errors.has('publishedAtBlogDate') || errors.has('publishedAtBlogTime')) {
            post.set('publishedAtBlogTZ', post.get('publishedAtUTC'));
            post.validate({attribute: 'publishedAtBlog'});
        }

        this.setSidebarWidthVariable(0);
    },

    actions: {
        showSubview(subview) {
            this.set('isViewingSubview', true);
            this.set('subview', subview);
        },

        closeSubview() {
            this.set('isViewingSubview', false);
            this.set('subview', null);
        },

        discardEnter() {
            return false;
        },

        toggleFeatured() {
            this.toggleProperty('post.featured');

            // If this is a new post.  Don't save the post.  Defer the save
            // to the user pressing the save button
            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        /**
         * triggered by user manually changing slug
         */
        updateSlug(newSlug) {
            return this.updateSlugTask
                .perform(newSlug)
                .catch((error) => {
                    this.showError(error);
                    this.post.rollbackAttributes();
                });
        },

        setPublishedAtBlogDate(date) {
            let post = this.post;
            let dateString = moment(date).format('YYYY-MM-DD');

            post.get('errors').remove('publishedAtBlogDate');

            if (post.get('isNew') || date === post.get('publishedAtBlogDate')) {
                post.validate({property: 'publishedAtBlog'});
            } else {
                post.set('publishedAtBlogDate', dateString);
                return this.savePostTask.perform();
            }
        },

        async setVisibility(segment) {
            this.post.set('visibilityFilter', segment);
            try {
                await this.post.validate({property: 'visibility'});
                await this.post.validate({property: 'visibilityFilter'});
                if (this.post.changedAttributes().visibilityFilter) {
                    await this.savePostTask.perform();
                }
            } catch (e) {
                if (!e) {
                    // validation error
                    return;
                }

                throw e;
            }
        },

        setPublishedAtBlogTime(time) {
            let post = this.post;

            post.get('errors').remove('publishedAtBlogDate');

            if (post.get('isNew') || time === post.get('publishedAtBlogTime')) {
                post.validate({property: 'publishedAtBlog'});
            } else {
                post.set('publishedAtBlogTime', time);
                return this.savePostTask.perform();
            }
        },

        setCustomExcerpt(excerpt) {
            let post = this.post;
            let currentExcerpt = post.get('customExcerpt');

            if (excerpt === currentExcerpt) {
                return;
            }

            post.set('customExcerpt', excerpt);

            return post.validate({property: 'customExcerpt'}).then(() => this.savePostTask.perform());
        },

        setHeaderInjection(code) {
            let post = this.post;
            let currentCode = post.get('codeinjectionHead');

            if (code === currentCode) {
                return;
            }

            post.set('codeinjectionHead', code);

            return post.validate({property: 'codeinjectionHead'}).then(() => this.savePostTask.perform());
        },

        setFooterInjection(code) {
            let post = this.post;
            let currentCode = post.get('codeinjectionFoot');

            if (code === currentCode) {
                return;
            }

            post.set('codeinjectionFoot', code);

            return post.validate({property: 'codeinjectionFoot'}).then(() => this.savePostTask.perform());
        },

        setMetaTitle(metaTitle) {
            // Grab the post and current stored meta title
            let post = this.post;
            let currentTitle = post.get('metaTitle');

            // If the title entered matches the stored meta title, do nothing
            if (currentTitle === metaTitle) {
                return;
            }

            // If the title entered is different, set it as the new meta title
            post.set('metaTitle', metaTitle);

            // Make sure the meta title is valid and if so, save it into the post
            return post.validate({property: 'metaTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setMetaDescription(metaDescription) {
            // Grab the post and current stored meta description
            let post = this.post;
            let currentDescription = post.get('metaDescription');

            // If the title entered matches the stored meta title, do nothing
            if (currentDescription === metaDescription) {
                return;
            }

            // If the title entered is different, set it as the new meta title
            post.set('metaDescription', metaDescription);

            // Make sure the meta title is valid and if so, save it into the post
            return post.validate({property: 'metaDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setCanonicalUrl(value) {
            // Grab the post and current stored meta description
            let post = this.post;
            let currentCanonicalUrl = post.canonicalUrl;

            // If the value entered matches the stored value, do nothing
            if (currentCanonicalUrl === value) {
                return;
            }

            // If the value supplied is different, set it as the new value
            post.set('canonicalUrl', value);

            // Make sure the value is valid and if so, save it into the post
            return post.validate({property: 'canonicalUrl'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setOgTitle(ogTitle) {
            // Grab the post and current stored facebook title
            let post = this.post;
            let currentTitle = post.get('ogTitle');

            // If the title entered matches the stored facebook title, do nothing
            if (currentTitle === ogTitle) {
                return;
            }

            // If the title entered is different, set it as the new facebook title
            post.set('ogTitle', ogTitle);

            // Make sure the facebook title is valid and if so, save it into the post
            return post.validate({property: 'ogTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setOgDescription(ogDescription) {
            // Grab the post and current stored facebook description
            let post = this.post;
            let currentDescription = post.get('ogDescription');

            // If the title entered matches the stored facebook description, do nothing
            if (currentDescription === ogDescription) {
                return;
            }

            // If the description entered is different, set it as the new facebook description
            post.set('ogDescription', ogDescription);

            // Make sure the facebook description is valid and if so, save it into the post
            return post.validate({property: 'ogDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setTwitterTitle(twitterTitle) {
            // Grab the post and current stored twitter title
            let post = this.post;
            let currentTitle = post.get('twitterTitle');

            // If the title entered matches the stored twitter title, do nothing
            if (currentTitle === twitterTitle) {
                return;
            }

            // If the title entered is different, set it as the new twitter title
            post.set('twitterTitle', twitterTitle);

            // Make sure the twitter title is valid and if so, save it into the post
            return post.validate({property: 'twitterTitle'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setTwitterDescription(twitterDescription) {
            // Grab the post and current stored twitter description
            let post = this.post;
            let currentDescription = post.get('twitterDescription');

            // If the description entered matches the stored twitter description, do nothing
            if (currentDescription === twitterDescription) {
                return;
            }

            // If the description entered is different, set it as the new twitter description
            post.set('twitterDescription', twitterDescription);

            // Make sure the twitter description is valid and if so, save it into the post
            return post.validate({property: 'twitterDescription'}).then(() => {
                if (post.get('isNew')) {
                    return;
                }

                return this.savePostTask.perform();
            });
        },

        setCoverImage(image) {
            this.set('post.featureImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearCoverImage() {
            this.set('post.featureImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        setOgImage(image) {
            this.set('post.ogImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearOgImage() {
            this.set('post.ogImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        setTwitterImage(image) {
            this.set('post.twitterImage', image);

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        clearTwitterImage() {
            this.set('post.twitterImage', '');

            if (this.get('post.isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                this.post.rollbackAttributes();
            });
        },

        changeAuthors(newAuthors) {
            let post = this.post;

            // return if nothing changed
            if (newAuthors.mapBy('id').join() === post.get('authors').mapBy('id').join()) {
                return;
            }

            post.set('authors', newAuthors);
            post.validate({property: 'authors'});

            // if this is a new post (never been saved before), don't try to save it
            if (post.get('isNew')) {
                return;
            }

            this.savePostTask.perform().catch((error) => {
                this.showError(error);
                post.rollbackAttributes();
            });
        },

        deletePost() {
            if (this.deletePost) {
                this.deletePost();
            }
        }
    },

    showError(error) {
        // TODO: remove null check once ValidationEngine has been removed
        if (error) {
            this.notifications.showAPIError(error);
        }
    },

    setSidebarWidthFromElement: action(function (element) {
        const width = element.getBoundingClientRect().width;
        this.setSidebarWidthVariable(width);
    }),

    setSidebarWidthVariable(width) {
        document.documentElement.style.setProperty('--editor-sidebar-width', `${width}px`);
    }
});
<|endoftext|>


<commit_before>/**
 * DOMson v1.0.1
 * (c) 2016 James, EV-Labs
 * License: MIT
 */

'use strict';

var DOMson = (function($, undefined) {
    var VERSION = '1.0.1';

    var _keywords = {
        exportKey: 'export-key',
        required: 'required',
        modified: 'modified'
    };

    var _options = {
        exportModifiedData: true
    };

    var _eventAttached = false;
    var _domObject = null;

    /**
     * Set up a custom configuration
     */
    function _setupConfig(config) {
        if (config === undefined) {
            return;
        }

        var keywords = config.keywords;

        if (keywords !== undefined) {
            _keywords = $.extend(_keywords, keywords);
        }

        var options = config.options;

        if (options !== undefined) {
            _options = $.extend(_options, options);
        }
    }

    /**
     * Attach an event handler for detecting value change of input
     */
    function _attachEvent(domElement) {
        if (domElement !== undefined) {
            _domObject = $(domElement);
        } else {
            _domObject = $('*');
        }

        _domObject.find(":input, select").on('change', function() {
            var type = $(this).attr('type');
            var dataKey = $(this).attr(_keywords.exportKey);

            if (dataKey !== undefined && dataKey !== '') {
                if (type === 'checkbox') {
                    $('[' + _keywords.exportKey + '=' + dataKey + ']').attr(_keywords.modified, true);
                } else {
                    $(this).attr(_keywords.modified, true);
                }
            }
        });

        _eventAttached = true;
    }

    /**
     * Detach an event handler
     */
    function _detachEvent() {
        if (_domObject !== null && _eventAttached) {
            _domObject.off('change');

            _domObject = null;
            _eventAttached = false;
        }
    }

    /**
     * Validate all elements that have an attribute data-required
     */
    function _validate() {
        var result = {};

        _domObject.find('[' + _keywords.required + ']').each(function(index, item) {
            var dataKey = $(item).attr(_keywords.exportKey);

            if (dataKey === undefined || dataKey === '') {
                return;
            }

            var dataRequired = $(item).attr(_keywords.required);
            var isDataRequired = (dataRequired !== undefined && dataRequired !== false);

            if (isDataRequired === false) {
                return;
            }

            if (item.value === undefined || item.value === '') {
                if (result[dataKey] === undefined) {
                    result[dataKey] = [];
                }

                if ($.inArray(result[dataKey], 'empty') === -1) {
                    result[dataKey].push('empty')
                }
            }
        });

        return result;
    }

    /**
     * Traverse attached DOM elements
     */
    function _traverse(current, output) {
        $(current).children().each(function(index, item) {
            var type = $(item).attr('type');
            var tagName = item['tagName'].toUpperCase();

            var dataKey = $(item).attr(_keywords.exportKey);
            var isDataKeyExists = (dataKey !== undefined && dataKey !== '');

            if (isDataKeyExists === false) {
                _traverse(item, output);
                return;
            }

            if (tagName === 'DIV') {
                var nextOutput = {};

                _traverse(item, nextOutput);

                if (Object.keys(nextOutput).length !== 0) {
                    if (output[dataKey] !== undefined) {
                        if (typeof output[dataKey] === typeof {}) {
                            var legacyObject = output[dataKey];

                            output[dataKey] = [];
                            output[dataKey].push(legacyObject);
                        }
                        output[dataKey].push(nextOutput);

                    } else {
                        output[dataKey] = nextOutput;
                    }
                }
                return;
            }

            var dataModified = $(item).attr(_keywords.modified);
            var isDataModified = (dataModified !== undefined && dataModified !== false);

            if (isDataModified === false && _options.exportModifiedData) {
              return;
            }

            if (tagName === 'SELECT') {
                output[dataKey] = item.options[item.selectedIndex].value;

            } else if (tagName === 'INPUT') {
                if (type === 'checkbox') {
                    if (output[dataKey] === undefined) {
                        output[dataKey] = [];
                    }

                    var checked = $(item).prop('checked');

                    if (checked !== undefined && checked === true) {
                        output[dataKey].push(item.value);
                    }

                } else if (type === 'radio') {
                    output[dataKey] = '';

                    var checked = $(item).prop('checked');

                    if (checked !== undefined && checked === true) {
                        output[dataKey] = item.value;
                    }

                } else {
                    output[dataKey] = item.value;
                }
            }
        });
    }

    return {
        attach: function(domElement, config) {
            if (_eventAttached === true) {
                console.error('DOMson already attached');
                return;
            }

            _setupConfig(config);
            _attachEvent(domElement);

            return (_eventAttached === true);
        },

        detach: function() {
            _detachEvent();

            return (_eventAttached === false);
        },

        validate: function() {
            if (_domObject === null || _eventAttached === false) {
                console.error('DOMson is not attached');
                return;
            }

            return _validate();
        },

        export: function() {
            if (_domObject === null || _eventAttached === false) {
                console.error('DOMson is not attached');
                return;
            }

            var resultObject = {};
            _traverse(_domObject, resultObject);

            return resultObject;
        }
    }
})(jQuery);
<commit_msg>Fix bug for type comparison in traverse method<commit_after>/**
 * DOMson v1.0.1
 * (c) 2016 James, EV-Labs
 * License: MIT
 */

'use strict';

var DOMson = (function($, undefined) {
    var VERSION = '1.0.1';

    var _keywords = {
        exportKey: 'export-key',
        required: 'required',
        modified: 'modified'
    };

    var _options = {
        exportModifiedData: true
    };

    var _eventAttached = false;
    var _domObject = null;

    /**
     * Set up a custom configuration
     */
    function _setupConfig(config) {
        if (config === undefined) {
            return;
        }

        var keywords = config.keywords;

        if (keywords !== undefined) {
            _keywords = $.extend(_keywords, keywords);
        }

        var options = config.options;

        if (options !== undefined) {
            _options = $.extend(_options, options);
        }
    }

    /**
     * Attach an event handler for detecting value change of input
     */
    function _attachEvent(domElement) {
        if (domElement !== undefined) {
            _domObject = $(domElement);
        } else {
            _domObject = $('*');
        }

        _domObject.find(":input, select").on('change', function() {
            var type = $(this).attr('type');
            var dataKey = $(this).attr(_keywords.exportKey);

            if (dataKey !== undefined && dataKey !== '') {
                if (type === 'checkbox') {
                    $('[' + _keywords.exportKey + '=' + dataKey + ']').attr(_keywords.modified, true);
                } else {
                    $(this).attr(_keywords.modified, true);
                }
            }
        });

        _eventAttached = true;
    }

    /**
     * Detach an event handler
     */
    function _detachEvent() {
        if (_domObject !== null && _eventAttached) {
            _domObject.off('change');

            _domObject = null;
            _eventAttached = false;
        }
    }

    /**
     * Validate all elements that have an attribute data-required
     */
    function _validate() {
        var result = {};

        _domObject.find('[' + _keywords.required + ']').each(function(index, item) {
            var dataKey = $(item).attr(_keywords.exportKey);

            if (dataKey === undefined || dataKey === '') {
                return;
            }

            var dataRequired = $(item).attr(_keywords.required);
            var isDataRequired = (dataRequired !== undefined && dataRequired !== false);

            if (isDataRequired === false) {
                return;
            }

            if (item.value === undefined || item.value === '') {
                if (result[dataKey] === undefined) {
                    result[dataKey] = [];
                }

                if ($.inArray(result[dataKey], 'empty') === -1) {
                    result[dataKey].push('empty')
                }
            }
        });

        return result;
    }

    /**
     * Traverse attached DOM elements
     */
    function _traverse(current, output) {
        $(current).children().each(function(index, item) {
            var type = $(item).attr('type');
            var tagName = item['tagName'].toUpperCase();

            var dataKey = $(item).attr(_keywords.exportKey);
            var isDataKeyExists = (dataKey !== undefined && dataKey !== '');

            if (isDataKeyExists === false) {
                _traverse(item, output);
                return;
            }

            if (tagName === 'DIV') {
                var nextOutput = {};

                _traverse(item, nextOutput);

                if (Object.keys(nextOutput).length !== 0) {
                    if (output[dataKey] !== undefined) {
                        if (Array.isArray(output[dataKey]) === false) {
                            var legacyObject = output[dataKey];

                            output[dataKey] = [];
                            output[dataKey].push(legacyObject);
                        }
                        output[dataKey].push(nextOutput);

                    } else {
                        output[dataKey] = nextOutput;
                    }
                }
                return;
            }

            var dataModified = $(item).attr(_keywords.modified);
            var isDataModified = (dataModified !== undefined && dataModified !== false);

            if (isDataModified === false && _options.exportModifiedData) {
              return;
            }

            if (tagName === 'SELECT') {
                output[dataKey] = item.options[item.selectedIndex].value;

            } else if (tagName === 'INPUT') {
                if (type === 'checkbox') {
                    if (output[dataKey] === undefined) {
                        output[dataKey] = [];
                    }

                    var checked = $(item).prop('checked');

                    if (checked !== undefined && checked === true) {
                        output[dataKey].push(item.value);
                    }

                } else if (type === 'radio') {
                    output[dataKey] = '';

                    var checked = $(item).prop('checked');

                    if (checked !== undefined && checked === true) {
                        output[dataKey] = item.value;
                    }

                } else {
                    output[dataKey] = item.value;
                }
            }
        });
    }

    return {
        attach: function(domElement, config) {
            if (_eventAttached === true) {
                console.error('DOMson already attached');
                return;
            }

            _setupConfig(config);
            _attachEvent(domElement);

            return (_eventAttached === true);
        },

        detach: function() {
            _detachEvent();

            return (_eventAttached === false);
        },

        validate: function() {
            if (_domObject === null || _eventAttached === false) {
                console.error('DOMson is not attached');
                return;
            }

            return _validate();
        },

        export: function() {
            if (_domObject === null || _eventAttached === false) {
                console.error('DOMson is not attached');
                return;
            }

            var resultObject = {};
            _traverse(_domObject, resultObject);

            return resultObject;
        }
    }
})(jQuery);
<|endoftext|>


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

/* BEGIN NPM-GLUE */

// This code block will be striped when building the stand-alone version.
// When using `npm` this exports the correct functions in order to be easily
// imported in the correct scope, without leaking to the global scope.

var window = typeof window !== "undefined" && window || {};
module.exports = window;

/* END NPM-GLUE */

// ((global) => {

  /**
   * Create a VNode element
   *
   * @param {String|Function} element - The tag name or the component to render
   * @param {Object} [props] - The object properties
   * @param {Array} [children] - The child VNode elements
   * @returns {VNode} Returns a virtual DOM instance
   */
  let createElement = (element, props={}, ...children) => (
    element = ({                                                      // The reference of `element` will be kept
                                                                      // in the object so we are safe to replace it

      $: element,                                                     // 'E' holds the name or function passed as
                                                                      // first argument

      a: (props.$ || props.trim || props.map)                         // If the props argument is a renderable VNode,
                                                                      // a string or an array, then

          ? {c: [].concat(props, ...children)}                        // ... prepend it to the children
          : (props.c = [].concat(...children)) && props               // ... otherwise append 'C' to the property
                                                                      // the .concat ensures that arrays of children
                                                                      // will be flattened into a single array.

      }
    ),

    element.a.className = element.a.className || '',                  // Ensure `className` always exist, since this
                                                                      // way we can cleanly replace className when
                                                                      // removed.
    element
  ),z=(a=[],b,c)=>a.map(e => e(b,c))

  /**
   * Render a VNode in the DOM
   *
   * @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render
   * @param {HTLDomElement}
   */
  let render = window.R = (
    vnodes,                                                           // 1. The vnode tree to render
    dom,                                                              // 2. The DOMElement where to render into

    _npath,                                                           // a. The current state path
    _children=dom.childNodes,                                         // b. Shorthand for accessing the children
    _c=0                                                              // c. Counter for processed children

  ) => {

    (vnodes.map ? vnodes : [vnodes]).map(                             // Cast `vnodes` to array if nor already

                                                                      // In this `map` loop we ensure that the DOM
                                                                      // elements correspond to the correct virtual
                                                                      // node elements.
      (
        vnode,                                                        // 1. We handle the vnode from the array
        index,                                                        // 2. And the index

        _unused1,                                                     // We don't handle the array, but we need the
                                                                      // placeholder for the local variables after

        _path=_npath+' '+index,                                       // a. The state path of this vnode
        _path_state=wrapClassProxy[_path] || [{}, vnode.$],           // b. Get the state record for this path
        _state=(                                                      // c. Update and get the state record
          wrapClassProxy[_path] =                                     //    The record is an the following format:
            _path_state[1] != vnode.$                                 //  [ {state object},
            ? [{}, vnode.$]                                           //    'vnode element' ]
            : _path_state                                             //    The second component is needed in order to
        ),                                                            //    reset the state if the component has changed
        _child=_children[_c++],                                       // d. Get the next DOM child + increment counter
        _hooks={a:vnode.$,m:[],u:[],d:[]},
        _new_dom                                                      // e. The new DOM element placeholder

      ) => {

        /* Expand functional Components */

        (vnode.$ || _unused1).call &&                                 // (This expands to : vnode.$ && vnode.$.call &&)
                                                                      // If the vnode is a functional component, expand
          (vnode = vnode.$(                                           // it and replace the current vnode variable.

            vnode.a,                                                  // 1. The component properties
            { state: _state[0],                                                // 2. The stateful component state

            setState: (newState) =>                                             // 3. The setState function

              Object.assign(                                          // First we update the state part of the record
                _state[0],                                            // Note: When we defined the variable we kept the
                newState                                              //       reference to the record array
              ) &&

              render(                                                 // We then trigger the same render cycle that will
                vnodes,                                               // update the DOM
                dom,
                _npath
              ),

              hooks: _hooks }

          ));

        /* Create new DOM element */

        _new_dom =                                                    // We prepare the new DOM element in advance in
          vnode.trim                                                  // order to spare a few comparison bytes
            ? document.createTextNode(vnode)
            : document.createElement(vnode.$);

        /* Keep or replace the previous DOM element */

        _new_dom =
          _child                                                      // If we have a previous child we first check if
            ? (_child.$ != vnode.$ && _child.data != vnode)           // the VNode element or the text are the same

              ? (
                  dom.replaceChild(                                   // - If not, we replace the old element with the
                    _new_dom,                                         //   new one.
                    _child
                  ),
                  _new_dom                                            //   ... and we make sure we return the new DOM
                )
              : _child                                                // - If it's the same, we keep the old child

            : dom.appendChild(                                        // mount lifecycle method and append
                _new_dom
              );

        /* Call lifecycle methods */

        z(
          _child
            ? _child.a != _hooks.a
              ? (z(_child.u), _hooks.m)
              : _hooks.d
            : _hooks.m
        ,_new_dom, _child);

        /* Update Element */

        Object.assign(_new_dom, vnode, _hooks);                       // Keep the following information in the DOM:
                                                                      // - $ : The tag name from the vnode. We use this
                                                                      //       instead of the .tagName because some
                                                                      //       browsers convert it to capital-case
                                                                      // - u : The `didUnmount` hook that is called when
                                                                      //       the DOM element is removed
                                                                      //
                                                                      // By assigning the entire _hooks and vnode
                                                                      // objects we expose some unneeded properties, but
                                                                      // it occupies less space than assigning $ and u
                                                                      // individually.

        vnode.trim
          ? _new_dom.data = vnode                                     // - String nodes update only the text
          : Object.keys(vnode.a).map(                                 // - Element nodes have properties
              (
                key                                                   // 1. The property name
              ) =>

                key == 'style' ?                                      // The 'style' property is an object and must be
                                                                      // applied recursively.
                  Object.assign(
                    _new_dom[key],                                    // '[key]' is shorter than '.style'
                    vnode.a[key]
                  )

                : (_new_dom[key] !== vnode.a[key] &&                  // All properties are applied directly to DOM, as
                  (_new_dom[key] = vnode.a[key]))                     // long as they are different than ther value in the
                                                                      // instance. This includes `onXXX` event handlers.

            ) &&
            render(                                                   // Only if we have an element (and not  text node)
              vnode.a.c,                                              // we recursively continue rendering into it's
              _new_dom,                                               // child nodes.
              _path
            )
      }
    );

    /* Remove extraneous nodes */

    while (_children[_c]) {                                           // The _c property keeps track of the number of
                                                                      // elements in the VDom. If there are more child
                                                                      // nodes in the DOM, we remove them.

      z(_children[_c].u)                           // We then call the unmount lifecycle method for the
                                                                      // elements that will be removed

      render(                                                         // Remove child an trigger a recursive child removal
        [],                                                           // in order to call the correct lifecycle methods in our
        dom.removeChild(_children[_c])                                // deep children too.
      )
    }

  }

  /**
   * Helper function that wraps an element shorthand function with a proxy
   * that can be used to append class names to the instance.
   *
   * The result is wrapped with the same function, creating a chainable mechanism
   * for appending classes.
   *
   * @param {function} factoryFn - The factory function to call for creating vnode
   */
  let wrapClassProxy = (factoryFn) =>
    new Proxy(                                                        // We are creating a proxy object for every tag in
                                                                      // order to be able to customize the class name
                                                                      // via a shorthand call.
      factoryFn,
      {
        get: (targetFn, className, _instance) =>
          wrapClassProxy(
            (...args) => (
              (_instance=targetFn(...args))                           // We first create the Virtual DOM instance by
                                                                      // calling the wrapped factory function

                .a.className += ' ' + className,                      // And then we assign the class name,
                                                                      // concatenating to the previous value

              _instance                                               // And finally we return the instance
            )
          )
      }
    )

  /**
   * Expose as `H` a proxy around the createElement function that can either be used
   * either as a function (ex. `H('div')`, or as a proxied method `H.div()` for creating
   * virtual DOM elements.
   */
  window.H = createElement;

// })(window);
<commit_msg>Update dotdom.js<commit_after>/**
 * .dom - A Tiny VDom Template Engine
 *
 * Copyright 2017 Ioannis Charalampidis (wavesoft)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* BEGIN NPM-GLUE */

// This code block will be striped when building the stand-alone version.
// When using `npm` this exports the correct functions in order to be easily
// imported in the correct scope, without leaking to the global scope.

var window = typeof window !== "undefined" && window || {};
module.exports = window;

/* END NPM-GLUE */

// ((global) => {

  /**
   * Create a VNode element
   *
   * @param {String|Function} element - The tag name or the component to render
   * @param {Object} [props] - The object properties
   * @param {Array} [children] - The child VNode elements
   * @returns {VNode} Returns a virtual DOM instance
   */
  let createElement = (element, props={}, ...children) => (
    element = ({                                                      // The reference of `element` will be kept
                                                                      // in the object so we are safe to replace it

      $: element,                                                     // 'E' holds the name or function passed as
                                                                      // first argument

      a: (props.$ || props.trim || props.map)                         // If the props argument is a renderable VNode,
                                                                      // a string or an array, then

          ? {c: [].concat(props, ...children)}                        // ... prepend it to the children
          : (props.c = [].concat(...children)) && props               // ... otherwise append 'C' to the property
                                                                      // the .concat ensures that arrays of children
                                                                      // will be flattened into a single array.

      }
    ),

    element.a.className = element.a.className || '',                  // Ensure `className` always exist, since this
                                                                      // way we can cleanly replace className when
                                                                      // removed.
    element
  ),z=(a=[],b,c)=>a.map(e => e(b,c))

  /**
   * Render a VNode in the DOM
   *
   * @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render
   * @param {HTLDomElement}
   */
  let render = window.R = (
    vnodes,                                                           // 1. The vnode tree to render
    dom,                                                              // 2. The DOMElement where to render into

    _npath,                                                           // a. The current state path
    _children=dom.childNodes,                                         // b. Shorthand for accessing the children
    _c=0                                                              // c. Counter for processed children

  ) => {

    (vnodes.map ? vnodes : [vnodes]).map(                             // Cast `vnodes` to array if nor already

                                                                      // In this `map` loop we ensure that the DOM
                                                                      // elements correspond to the correct virtual
                                                                      // node elements.
      (
        vnode,                                                        // 1. We handle the vnode from the array
        index,                                                        // 2. And the index

        _unused1,                                                     // We don't handle the array, but we need the
                                                                      // placeholder for the local variables after

        _path=_npath+' '+index,                                       // a. The state path of this vnode
        _path_state=wrapClassProxy[_path] || [{}, vnode.$],           // b. Get the state record for this path
        _state=(                                                      // c. Update and get the state record
          wrapClassProxy[_path] =                                     //    The record is an the following format:
            _path_state[1] != vnode.$                                 //  [ {state object},
            ? [{}, vnode.$]                                           //    'vnode element' ]
            : _path_state                                             //    The second component is needed in order to
        ),                                                            //    reset the state if the component has changed
        _child=_children[_c++],                                       // d. Get the next DOM child + increment counter
        _hooks={a:vnode.$,m:[],u:[],d:[]},
        _new_dom                                                      // e. The new DOM element placeholder

      ) => {

        /* Expand functional Components */

        (vnode.$ || _unused1).call &&                                 // (This expands to : vnode.$ && vnode.$.call &&)
                                                                      // If the vnode is a functional component, expand
          (vnode = vnode.$(                                           // it and replace the current vnode variable.

            vnode.a,                                                  // 1. The component properties
            _state[0],                                                // 2. The stateful component state

            (newState) =>                                             // 3. The setState function

              Object.assign(                                          // First we update the state part of the record
                _state[0],                                            // Note: When we defined the variable we kept the
                newState                                              //       reference to the record array
              ) &&

              render(                                                 // We then trigger the same render cycle that will
                vnodes,                                               // update the DOM
                dom,
                _npath
              ),

              _hooks

          ));

        /* Create new DOM element */

        _new_dom =                                                    // We prepare the new DOM element in advance in
          vnode.trim                                                  // order to spare a few comparison bytes
            ? document.createTextNode(vnode)
            : document.createElement(vnode.$);

        /* Keep or replace the previous DOM element */

        _new_dom =
          _child                                                      // If we have a previous child we first check if
            ? (_child.$ != vnode.$ && _child.data != vnode)           // the VNode element or the text are the same

              ? (
                  dom.replaceChild(                                   // - If not, we replace the old element with the
                    _new_dom,                                         //   new one.
                    _child
                  ),
                  _new_dom                                            //   ... and we make sure we return the new DOM
                )
              : _child                                                // - If it's the same, we keep the old child

            : dom.appendChild(                                        // mount lifecycle method and append
                _new_dom
              );

        /* Call lifecycle methods */

        z(
          _child
            ? _child.a != _hooks.a
              ? (z(_child.u), _hooks.m)
              : _hooks.d
            : _hooks.m
        ,_new_dom, _child);

        /* Update Element */

        Object.assign(_new_dom, vnode, _hooks);                       // Keep the following information in the DOM:
                                                                      // - $ : The tag name from the vnode. We use this
                                                                      //       instead of the .tagName because some
                                                                      //       browsers convert it to capital-case
                                                                      // - u : The `didUnmount` hook that is called when
                                                                      //       the DOM element is removed
                                                                      //
                                                                      // By assigning the entire _hooks and vnode
                                                                      // objects we expose some unneeded properties, but
                                                                      // it occupies less space than assigning $ and u
                                                                      // individually.

        vnode.trim
          ? _new_dom.data = vnode                                     // - String nodes update only the text
          : Object.keys(vnode.a).map(                                 // - Element nodes have properties
              (
                key                                                   // 1. The property name
              ) =>

                key == 'style' ?                                      // The 'style' property is an object and must be
                                                                      // applied recursively.
                  Object.assign(
                    _new_dom[key],                                    // '[key]' is shorter than '.style'
                    vnode.a[key]
                  )

                : (_new_dom[key] !== vnode.a[key] &&                  // All properties are applied directly to DOM, as
                  (_new_dom[key] = vnode.a[key]))                     // long as they are different than ther value in the
                                                                      // instance. This includes `onXXX` event handlers.

            ) &&
            render(                                                   // Only if we have an element (and not  text node)
              vnode.a.c,                                              // we recursively continue rendering into it's
              _new_dom,                                               // child nodes.
              _path
            )
      }
    );

    /* Remove extraneous nodes */

    while (_children[_c]) {                                           // The _c property keeps track of the number of
                                                                      // elements in the VDom. If there are more child
                                                                      // nodes in the DOM, we remove them.

      z(_children[_c].u)                           // We then call the unmount lifecycle method for the
                                                                      // elements that will be removed

      render(                                                         // Remove child an trigger a recursive child removal
        [],                                                           // in order to call the correct lifecycle methods in our
        dom.removeChild(_children[_c])                                // deep children too.
      )
    }

  }

  /**
   * Helper function that wraps an element shorthand function with a proxy
   * that can be used to append class names to the instance.
   *
   * The result is wrapped with the same function, creating a chainable mechanism
   * for appending classes.
   *
   * @param {function} factoryFn - The factory function to call for creating vnode
   */
  let wrapClassProxy = (factoryFn) =>
    new Proxy(                                                        // We are creating a proxy object for every tag in
                                                                      // order to be able to customize the class name
                                                                      // via a shorthand call.
      factoryFn,
      {
        get: (targetFn, className, _instance) =>
          wrapClassProxy(
            (...args) => (
              (_instance=targetFn(...args))                           // We first create the Virtual DOM instance by
                                                                      // calling the wrapped factory function

                .a.className += ' ' + className,                      // And then we assign the class name,
                                                                      // concatenating to the previous value

              _instance                                               // And finally we return the instance
            )
          )
      }
    )

  /**
   * Expose as `H` a proxy around the createElement function that can either be used
   * either as a function (ex. `H('div')`, or as a proxied method `H.div()` for creating
   * virtual DOM elements.
   */
  window.H = createElement;

// })(window);
<|endoftext|>