Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • master
1 result

Target

Select target project
No results found
Select Git revision
  • master
1 result
Show changes
1000 files
+ 219864
66
Compare changes
  • Side-by-side
  • Inline

Files

.gitignore

0 → 100644
+325 −0
Original line number Diff line number Diff line
/development/build/
node_modules/


development/temp/
development/log/
/application/source/server/web/node_modules/


##################### IDE / Tools ##


# @see https://github.com/github/gitignore/

# PHPUnit
.phpunit.result.cache

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

.cache

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Cloud9 IDE - http://c9.io
.c9revisions
.c9

##Exclipse
.metadata
#bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
#local.properties
.settings/
.loadpath
.recommenders

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# CDT- autotools
.autotools

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Annotation Processing
.apt_generated/
.apt_generated_test/

# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

##Kate
# Swap Files #
.*.kate-swp
.swp.*

## KDEDevelop
*.kdev4
.kdev4/

## LibreOffice
# LibreOffice locks
.~lock.*#


### LINUX
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

## Patches
*.orig
*.rej

# Private key
*.ppk

## Sublime
# Cache files for Sublime Text
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache

# Workspace files are user-specific
*.sublime-workspace

# Project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using Sublime Text
# *.sublime-project

# SFTP configuration file
sftp-config.json
sftp-config-alt*.json

# Package control specific files
Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
Package Control.merged-ca-bundle
Package Control.user-ca-bundle
oscrypto-ca-bundle.crt
bh_unicode_properties.cache

# Sublime-github package stores a github token in this file
# https://packagecontrol.io/packages/sublime-github
GitHub.sublime-settings

## VIM
# Swap
[._]*.s[a-v][a-z]
!*.svg  # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

# gitignore template for AWS Serverless Application Model project
# website: https://docs.aws.amazon.com/serverless-application-model

# Ignore build folder
.aws-sam/

# Netbeans
**/nbproject/private/
**/nbproject/Makefile-*.mk
**/nbproject/Package-*.bash
build/
nbbuild/
dist/
nbdist/
.nb-gradle/



# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

## GOLANG
go.work
Original line number Diff line number Diff line
@@ -3,7 +3,17 @@
  <component name="Go" enabled="true" />
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$" />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/application/source" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/command" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/config" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/server" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/server/web" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/server/web/app" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/application/source/utils" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/development/examples" type="java-resource" />
      <excludeFolder url="file://$MODULE_DIR$/development/build" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
  <configuration default="false" name="minerva serve" type="GoApplicationRunConfiguration" factoryName="Go Application">
    <module name="minerva" />
    <working_directory value="$PROJECT_DIR$/application/source" />
    <parameters value="serve --config=../../development/examples/theme1/config.yaml" />
    <parameters value="serve --config=../../development/examples/theme1/config.yaml --path=../../development/examples/theme1/" />
    <kind value="DIRECTORY" />
    <directory value="$PROJECT_DIR$/application/source" />
    <filePath value="$PROJECT_DIR$" />
Original line number Diff line number Diff line
@@ -30,4 +30,10 @@ func intiParser() {
		config.InitializeConfigFromFile(def.Config)
	}

	cfg := config.GetConfiguration()

	if def.Serve.Path != "" {
		cfg.Server.Path = def.Serve.Path
	}

}
Original line number Diff line number Diff line
@@ -5,24 +5,6 @@ type options struct {
	Version struct {
	} `command:"version"`
	Serve struct {
		Path string `short:"p" long:"path" description:"Path to the project files"`
	} `command:"serve"`
}

//// DebugLevel logs are typically voluminous, and are usually disabled in production.
// DebugLevel = zapcore.DebugLevel
//// InfoLevel is the default logging priority.
// InfoLevel = zapcore.InfoLevel
//// WarnLevel logs are more important than Info, but don't need individual
//// human review.
// WarnLevel = zapcore.WarnLevel
//// ErrorLevel logs are high-priority. If an application is running smoothly,
//// it shouldn't generate any error-level logs.
// ErrorLevel = zapcore.ErrorLevel
//// DPanicLevel logs are particularly important errors. In development the
//// logger panics after writing the message.
// DPanicLevel = zapcore.DPanicLevel
//// PanicLevel logs a message, then panics.
// PanicLevel = zapcore.PanicLevel
//// FatalLevel logs a message, then calls os.Exit(1).
// FatalLevel = zapcore.FatalLevel
// ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ func Do() {
		printVersion()

	case "serve":

		serve(active)

	}
Original line number Diff line number Diff line
package config

import (
	"github.com/creasty/defaults"
	"github.com/kelseyhightower/envconfig"
	"gitlab.schukai.com/oss/minerva/utils"
	"gopkg.in/yaml.v3"
@@ -8,24 +9,44 @@ import (
	"path"
)

func GetServerPort() string {
	return configuration.Server.Port
}

type Configuration struct {
	//LogLevel string `yaml:"logLevel" envconfig:"LOG_LEVEL"`

	Server struct {
		Port string `yaml:"Port" envconfig:"SERVER_PORT" default:"80"`
		Port int    `yaml:"Port" envconfig:"SERVER_PORT" default:"8080"`
		Path string `yaml:"Path" envconfig:"SERVER_PATH" default:""`
	} `yaml:"Server"`
}

// SetDefaults implements defaults.Setter interface
func (s *Configuration) SetDefaults() {
	if defaults.CanUpdate(s.Server.Path) {
		path, err := os.Getwd()
		if err != nil {
			utils.PrintErrorAndExit("something went wrong: %s", err.Error())
		}
		s.Server.Path = path
	}
}

	Paths struct {
		Web string `yaml:"Web" envconfig:"PATH_WEB" default:"/srv/web/"`
	} `yaml:"Paths"`
func GetServerPort() int {
	return configuration.Server.Port
}

func GetServerPath() string {
	return configuration.Server.Path
}

var configuration *Configuration

func newConfiguration() *Configuration {

	config := &Configuration{}
	if err := defaults.Set(config); err != nil {
		utils.PrintErrorAndExit("we have a problem: %s", err.Error())
	}

	return config
}

func GetConfiguration() *Configuration {

	if configuration == nil {
@@ -63,7 +84,7 @@ func intiConfiguration() {
		return
	}

	configuration = &Configuration{}
	configuration = newConfiguration()

}

@@ -98,10 +119,10 @@ func readFile(filename string) {
		}
	}()

	c := Configuration{}
	err = yaml.Unmarshal([]byte(data), &c)
	c := newConfiguration()
	err = yaml.Unmarshal([]byte(data), c)

	configuration = &c
	configuration = c

	if err != nil {
		utils.PrintErrorAndExit(err.Error())
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ require (
)

require (
	github.com/creasty/defaults v1.6.0 // indirect
	github.com/go-chi/chi/v5 v5.0.7 // indirect
	github.com/google/uuid v1.3.0 // indirect
	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
Original line number Diff line number Diff line
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/creasty/defaults v1.6.0 h1:ltuE9cfphUtlrBeomuu8PEyISTXnxqkBIoQfXgv7BSc=
github.com/creasty/defaults v1.6.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+31 −0
Original line number Diff line number Diff line
package server

import (
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"net/http"
)

func appHandler() http.Handler {

	r := chi.NewRouter()
	r.Use(middleware.NoCache)

	r.Get("/editor", renderEditor)

	r.Handle("/web/node_modules/*",
		http.StripPrefix("/app", http.FileServer(http.FS(embeddedFiles))))

	//r.Get("/node_modules/*", serveNodeModules)

	//r.Route("/node_modules/*",  http.StripPrefix("/assets/",
	//	http.FileServer(http.Dir("assets/"))))
	//
	//http.Handle("/assets/", http.StripPrefix("/assets/",
	//	http.FileServer(http.Dir("assets/"))))

	//r.Post("/convert", doV1Convert)
	//r.Post("/create", doV1Create)

	return r
}
+49 −0
Original line number Diff line number Diff line
package server

import (
	"net/http"
)

func serveNodeModules(w http.ResponseWriter, r *http.Request) {

	content := lookUp(r.URL.Path)
	if content != nil {

		//w.Header().Add("Content-Type", "application/json")

		//mimeType := ""
		//
		//ext := filepath.Ext(r.URL.Path)
		//switch ext {
		//case ".htm", ".html":
		//	return "text/html"
		//case ".css":
		//	return "text/css"
		//case ".js":
		//	return "application/javascript"
		//
		//
		//
		//filetype := http.DetectContentType(content)
		//fmt.Println(filetype)

		w.Write(content)
		return
	}

	w.WriteHeader(404)

}

func renderEditor(w http.ResponseWriter, r *http.Request) {

	content, err := embeddedFiles.ReadFile("web/app/editor.html")
	if err != nil {
		logError("something went wrong: %s", err.Error())
		w.WriteHeader(500)
		return
	}

	w.Write(content)

}
+65 −0
Original line number Diff line number Diff line
package server

import (
	"net/http"
	"path"
	"strings"
)

//// FSHandler404 provides the function signature for passing to the FileServerWith404
//type handler404 = func(w http.ResponseWriter, r *http.Request) (doDefaultFileServe bool)

func fileServer(root string) http.Handler {

	fs := http.FileServer(http.Dir(root))

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		//make sure the url path starts with /
		upath := r.URL.Path
		if !strings.HasPrefix(upath, "/") {
			upath = "/" + upath
			r.URL.Path = upath
		}

		upath = path.Clean(upath)
		//
		////// attempt to open the file via the http.FileSystem
		////f, err := root.Open(upath)
		////// close if successfully opened
		////if f != nil {
		////	defer f.Close()
		////}
		//if !utils.DirectoryExists(root) {
		//	if os.IsNotExist(err) {
		//		// call handler
		//		if h404 != nil {
		//			doDefault := h404(w, r)
		//			if !doDefault {
		//				return
		//			}
		//		}
		//	}
		//}
		//
		//s, err := f.Stat()
		//
		//if err != nil {
		//	if os.IsNotExist(err) {
		//		// call handler
		//		if h404 != nil {
		//			doDefault := h404(w, r)
		//			if !doDefault {
		//				return
		//			}
		//		}
		//	}
		//}
		//
		//if s.IsDir() && false {
		//	return
		//}

		// default serve
		fs.ServeHTTP(w, r)
	})
}
Original line number Diff line number Diff line
@@ -8,6 +8,10 @@ import (
	"github.com/go-chi/chi/v5/middleware"
	"gitlab.schukai.com/oss/minerva/config"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"sync"
)

@@ -17,26 +21,45 @@ var (
	embeddedFiles embed.FS
)

func lookUp(path string) (content []byte) {
	// Zuerst im Verzeichnis /web/asserts schauen (wird beim build inkludiert)
	c, _ := embeddedFiles.ReadFile("web" + path)
func lookUp(p string) (content []byte) {

	c, _ := embeddedFiles.ReadFile("web" + p)
	if len(c) > 0 {
		return c
	}

	base, err := filepath.Abs(config.GetServerPath())
	if err != nil {
		logError("%s", err.Error())
		return nil
	}

func serveStaticFiles(next http.Handler) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
	filename := path.Clean(path.Join(base, p))

		content := lookUp(r.URL.Path)
		if len(content) > 0 {
			w.Write(content)
			return
	data, _ := os.ReadFile(filename)
	if len(data) > 0 {
		return data
	}

	return nil
}

		next.ServeHTTP(w, r)
//func serveStaticFiles() func(http.ResponseWriter, *http.Request) {
//	return func(w http.ResponseWriter, r *http.Request) {
//
//		content := lookUp(r.URL.Path)
//		if content != nil {
//			w.Write(content)
//			return
//		}
//
//		w.WriteHeader(404)
//
//	}
//}
func redirectToApp() func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "app/editor", http.StatusTemporaryRedirect)
	}
}

@@ -57,21 +80,21 @@ func initRouting() *chi.Mux {
	/** x509: certificate signed by unknown authority */
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}

	mux := chi.NewRouter()
	router := chi.NewRouter()

	mux.Use(waitGroupMiddleware())
	router.Use(waitGroupMiddleware())

	mux.Use(RequestIDMiddleware)
	mux.Use(middleware.RealIP)
	mux.Use(middleware.Heartbeat("/ping"))
	router.Use(RequestIDMiddleware)
	router.Use(middleware.RealIP)
	router.Use(middleware.Heartbeat("/ping"))

	mux.Use(middleware.Compress(5))
	mux.Use(middleware.AllowContentType("application/json", "text/html"))
	mux.Use(loggerMiddleware)
	router.Use(middleware.Compress(5))
	router.Use(middleware.AllowContentType("application/json", "text/html"))
	router.Use(loggerMiddleware)

	//	mux.Mount("/api/v1", v1.V1Api(cfg))
	router.Mount("/app", appHandler())

	//mux.Get("/", func(w http.ResponseWriter, r *http.Request) {
	//router.Get("/", func(w http.ResponseWriter, r *http.Request) {
	//	if negotiator.New(r.Header).Type("text/html") == "text/html" {
	//		http.Redirect(w, r, "https://www.alvine.cloud/en/products/media-services/juno/", http.StatusPermanentRedirect)
	//	} else {
@@ -92,17 +115,30 @@ func initRouting() *chi.Mux {
	//	return nil
	//}

	//router.Get("/*", redirectToApp())
	//
	//router.Path("/whatever").Handler(func(writer http.ResponseWriter, req *http.Request) {
	//	http.Redirect(writer, req, "localhost:8080/whatever", http.StatusMovedPermanently)
	//	))
	//

	//d := http.Dir(p)
	//mux.Get("/*",
	//	serveStaticFiles(fileserver.FileServerWith404(d, func(w http.ResponseWriter, r *http.Request) (doDefaultFileServe bool) {
	//		error2.StandardErrorResponse(w, r, error2.ErrorResponse{
	//			Status: 404,
	//		})
	//		return false
	//	router.Get("/*",
	//serveStaticFiles(
	//	func(w http.ResponseWriter, r *http.Request) (doDefaultFileServe bool) {
	//
	//	}
	//
	//	})))
	//		)
	//		//serveStaticFiles(fileServerWith404("/tmp", func(w http.ResponseWriter, r *http.Request) (doDefaultFileServe bool) {
	//		//	error2.StandardErrorResponse(w, r, error2.ErrorResponse{
	//		//		Status: 404,
	//		//	})
	//		//	return false
	//		//
	//		//})))

	return mux
	return router

}

@@ -113,7 +149,7 @@ func Serve() {

	c := config.GetConfiguration()

	address := ":" + c.Server.Port
	address := ":" + strconv.FormatInt(int64(c.Server.Port), 10)
	logInfo("server listen on address %s", address)

	server := http.Server{
Original line number Diff line number Diff line
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>



 
    
    
    
    <link rel="stylesheet" href="/app/web/node_modules/grapesjs/dist/css/grapes.min.css">
    <link rel="stylesheet" href="/app/web/node_modules/grapesjs-preset-webpage/dist/grapesjs-preset-webpage.min.css">

    <script src="/app/web/node_modules/grapesjs/dist/grapes.min.js"></script>
    <script src="/app/web/node_modules/grapesjs-preset-webpage/dist/grapesjs-preset-webpage.min.js"></script>

    <style>
        body,
        html {<
            height: 100%;
            margin: 0;
            overflow: hidden;
        }

        #toast-container {
            font-size: 13px;
            font-weight: lighter;
        }

        #toast-container > div {
            opacity: 0.95;
        }

        #toast-container > div,
        #toast-container > div:hover {
            box-shadow: 0 0 12px rgba(0, 0, 0, 0.1);
            font-family: Helvetica, sans-serif;
        }

        /* LOGO VERSION */

        .gjs-pn-commands .gjs-pn-buttons,
        #gjs-pn-commands .gjs-pn-buttons {
            display: none;
        }

        .gjs-logo {
            height: 25px;
        }

        .gjs-logo-cont {
            position: relative;
            display: inline-block;
            top: 3px;
        }

        .gjs-logo-version {
            position: absolute;
            font-size: 10px;
            padding: 1px 7px;
            border-radius: 15px;
            bottom: 2px;
            right: -43px;
        }

        /* INFO PANEL */

        .gjs-mdl-dialog-sm {
            width: 300px;
        }

        #info-panel {
            line-height: 17px;
        }

        .info-panel-logo {
            display: block;
            height: 90px;
            margin: 0 auto;
            width: 90px;
        }

        .info-panel-logo path {
            stroke: #eee !important;
            stroke-width: 8 !important;
        }

        .info-panel-label {
            margin-bottom: 10px;
            font-size: 13px;
        }

        .info-panel-link {
            text-decoration: none;
        }

        /* ADS */

        .gjs-pn-panel#gjs-pn-views-container,
        .gjs-pn-panel.gjs-pn-views-container {
            height: calc(100% - 150px);
        }

        .ad-cont {
            position: absolute;
            right: 0;
            bottom: 0;
            z-index: 2;
            width: 15%;
            height: 150px;
        }

        #carbonads {
            font: caption;
            padding: 20px 10px;
        }

        .carbon-link {
            text-decoration: none;
            font: caption;
        }

        .carbon-img {
            float: right;
            margin-left: 10px;
        }

        .carbon-img img {
            border-radius: 3px;
            max-width: 100px !important;
            max-height: 77px;
        }

        .carbon-text {
            color: rgba(255, 255, 255, 0.75);
            text-decoration: none;
            font-weight: lighter;
        }

        .carbon-poweredby {
            color: rgba(255, 255, 255, 0.55);
            text-decoration: none;
            float: right;
        }

        .carbon-cta-c {
            text-align: right;
            padding-top: 5px;
        }

        .carbon-cta {
            display: inline-block;
            padding: 4px 10px;
            border-radius: 3px;
            font-weight: bold;
            font-size: 12px;
        }

        .gjs-block-label svg,
        .gjs-block__media svg {
            width: 54px;
        }

        /* Temporary fix #2490 */
        .gjs-clm-tag-status,
        .gjs-clm-tag-close {
            width: 12px;
            height: 12px;
        }
        .gjs-clm-tags-btn {
            width: 24px;
        }
        
    </style>


</head>
<body>


<div id="gjs"></div>
<div id="blocks"></div>

<script type="text/javascript">
    const editor = grapesjs.init({
        // Indicate where to init the editor. You can also pass an HTMLElement
        container: '#gjs',
        // Get the content for the canvas directly from the element
        // As an alternative we could use: `components: '<h1>Hello World Component!</h1>'`,
        fromElement: true,
        // Size of the editor
        height: '300px',
        width: 'auto',
        // Disable the storage manager for the moment
        storageManager: false,
        // Avoid any default panel
        panels: {defaults: []},
        blockManager: {
            appendTo: '#blocks',
            blocks: [
                {
                    id: 'section', // id is mandatory
                    label: '<b>Section</b>', // You can use HTML/SVG inside labels
                    attributes: {class: 'gjs-block-section'},
                    content: `<section>
          <h1>This is a simple title</h1>
          <div>This is just a Lorem text: Lorem ipsum dolor sit amet</div>
        </section>`,
                }, {
                    id: 'text',
                    label: 'Text',
                    content: '<div data-gjs-type="text">Insert your text here</div>',
                }, {
                    id: 'image',
                    label: 'Image',
                    // Select the component once it's dropped
                    select: true,
                    // You can pass components as a JSON instead of a simple HTML string,
                    // in this case we also use a defined component type `image`
                    content: {type: 'image'},
                    // This triggers `active` event on dropped components and the `image`
                    // reacts by opening the AssetManager
                    activate: true,
                }
            ]
        },
    });
</script>

</body>
</html>


Original line number Diff line number Diff line
{
  "name": "minerva",
  "version": "1.0.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "node_modules/backbone": {
      "version": "1.3.3",
      "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.3.3.tgz",
      "integrity": "sha1-TMgOp8sWMaxHSInOQPL4vGg7KZk=",
      "dependencies": {
        "underscore": ">=1.8.3"
      }
    },
    "node_modules/backbone-undo": {
      "version": "0.2.5",
      "resolved": "https://registry.npmjs.org/backbone-undo/-/backbone-undo-0.2.5.tgz",
      "integrity": "sha1-VbJSMPkDGcpiJGXomoAki4k8LOI=",
      "dependencies": {
        "backbone": "1.0.0 - 1.2.1",
        "underscore": "1.4.4 - 1.8.3"
      }
    },
    "node_modules/backbone-undo/node_modules/backbone": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.2.1.tgz",
      "integrity": "sha1-1yGcXtSeXhMdv/ryXJbW0sw8oD4=",
      "dependencies": {
        "underscore": ">=1.7.0"
      }
    },
    "node_modules/backbone-undo/node_modules/underscore": {
      "version": "1.8.3",
      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
      "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
    },
    "node_modules/codemirror": {
      "version": "5.65.3",
      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.3.tgz",
      "integrity": "sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ=="
    },
    "node_modules/codemirror-formatting": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/codemirror-formatting/-/codemirror-formatting-1.0.0.tgz",
      "integrity": "sha1-h5zB/dkBg0PB1VEXac5TYNcF6/I="
    },
    "node_modules/core-util-is": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
    },
    "node_modules/file-saver": {
      "version": "1.3.8",
      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
      "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
    },
    "node_modules/grapesjs": {
      "version": "0.18.4",
      "resolved": "https://registry.npmjs.org/grapesjs/-/grapesjs-0.18.4.tgz",
      "integrity": "sha512-mOkmOc7Q9OyuW4H8h4NDhFK1cxBnw/En+sD60enOKQrNy5lfua6Lh5I0oTm93RU6ahQy2QPzn1ZbZWAQaDCKQw==",
      "dependencies": {
        "backbone": "1.3.3",
        "backbone-undo": "^0.2.5",
        "codemirror": "^5.63.0",
        "codemirror-formatting": "^1.0.0",
        "promise-polyfill": "^8.1.3",
        "underscore": "^1.13.1"
      }
    },
    "node_modules/grapesjs-aviary": {
      "version": "0.1.2",
      "resolved": "https://registry.npmjs.org/grapesjs-aviary/-/grapesjs-aviary-0.1.2.tgz",
      "integrity": "sha512-F7sYQWj5s9eyUqoOX6q1z2J06AqgXhQqdjQhM8b5BptxlbDsvDTCbqSRmLGkEQKaIF1ca5o9FFi9eKBeUAfLEw==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-blocks-basic": {
      "version": "0.1.8",
      "resolved": "https://registry.npmjs.org/grapesjs-blocks-basic/-/grapesjs-blocks-basic-0.1.8.tgz",
      "integrity": "sha512-Iy5M4qGkEE2llb5ZFxitDolRJ3CrQkcK1SQIjRRlkWIAC99On3T/09iFR7NkpTBfsgfdNwcwnSVOMOhSnVLDtw==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-component-countdown": {
      "version": "0.1.3",
      "resolved": "https://registry.npmjs.org/grapesjs-component-countdown/-/grapesjs-component-countdown-0.1.3.tgz",
      "integrity": "sha512-TmxDETedUDv1jGqNLn6E768iyp+xAK/XcTId5WpLrmKPFB6aCuWqfTowhtSvyRJJs83OAFMiQIwIu+tEtSWOeA==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-navbar": {
      "version": "0.1.5",
      "resolved": "https://registry.npmjs.org/grapesjs-navbar/-/grapesjs-navbar-0.1.5.tgz",
      "integrity": "sha512-tVKPtzlTUNCpzk3lHW69II2NsSzLNQd3K32CXssWCP3M1V8fvc9jG/MQECBkd/gMS+DjldD4/4b00ri9F3tKSg==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-plugin-export": {
      "version": "0.1.5",
      "resolved": "https://registry.npmjs.org/grapesjs-plugin-export/-/grapesjs-plugin-export-0.1.5.tgz",
      "integrity": "sha512-BgvPY68vIDxVv0Z3v1TTikHseMxbfvUvhk5qUHq3a9kFmc2sFfNA6f5NHP9A4v3Tnd4kuwOd2aIn+QNutZrLRg==",
      "dependencies": {
        "file-saver": "^1.3.2",
        "jszip": "^3.0.0"
      },
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-plugin-filestack": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/grapesjs-plugin-filestack/-/grapesjs-plugin-filestack-0.1.1.tgz",
      "integrity": "sha512-zRwgHUZd7qDVk92dtizOHxnPiCjBBmsA2BHm0J86tz8uiXf6ou0OoQiB4x4UDewb9ipTkBI6X5nGh/0YET6WLw==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-plugin-forms": {
      "version": "0.3.6",
      "resolved": "https://registry.npmjs.org/grapesjs-plugin-forms/-/grapesjs-plugin-forms-0.3.6.tgz",
      "integrity": "sha512-JGIxky3if7FLlXBDuX2T2UoWQnvm55PcNp5P2qoX64zlA0+2npz9zbPcj9g1PUHXVPlTXwTr2qu5x3U/pb+xdw==",
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/grapesjs-preset-webpage": {
      "version": "0.1.11",
      "resolved": "https://registry.npmjs.org/grapesjs-preset-webpage/-/grapesjs-preset-webpage-0.1.11.tgz",
      "integrity": "sha512-36SuSPwu3a09R6YR31Sklmowz/LvzxzGM74WcPd8KPwbs1mAQ4fFuqWKkX/wv8+GhY58Se9EO42yQVMwsZzELA==",
      "dependencies": {
        "grapesjs-aviary": "^0.1.2",
        "grapesjs-blocks-basic": "^0.1.7",
        "grapesjs-component-countdown": "^0.1.2",
        "grapesjs-navbar": "^0.1.5",
        "grapesjs-plugin-export": "^0.1.5",
        "grapesjs-plugin-filestack": "^0.1.1",
        "grapesjs-plugin-forms": "^0.3.5"
      },
      "peerDependencies": {
        "grapesjs": "0.x"
      }
    },
    "node_modules/immediate": {
      "version": "3.0.6",
      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
    },
    "node_modules/inherits": {
      "version": "2.0.4",
      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
    },
    "node_modules/isarray": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
    },
    "node_modules/jszip": {
      "version": "3.9.1",
      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz",
      "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==",
      "dependencies": {
        "lie": "~3.3.0",
        "pako": "~1.0.2",
        "readable-stream": "~2.3.6",
        "set-immediate-shim": "~1.0.1"
      }
    },
    "node_modules/lie": {
      "version": "3.3.0",
      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
      "dependencies": {
        "immediate": "~3.0.5"
      }
    },
    "node_modules/pako": {
      "version": "1.0.11",
      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
    },
    "node_modules/process-nextick-args": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
    },
    "node_modules/promise-polyfill": {
      "version": "8.2.3",
      "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz",
      "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg=="
    },
    "node_modules/readable-stream": {
      "version": "2.3.7",
      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
      "dependencies": {
        "core-util-is": "~1.0.0",
        "inherits": "~2.0.3",
        "isarray": "~1.0.0",
        "process-nextick-args": "~2.0.0",
        "safe-buffer": "~5.1.1",
        "string_decoder": "~1.1.1",
        "util-deprecate": "~1.0.1"
      }
    },
    "node_modules/safe-buffer": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
    },
    "node_modules/set-immediate-shim": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/string_decoder": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
      "dependencies": {
        "safe-buffer": "~5.1.0"
      }
    },
    "node_modules/underscore": {
      "version": "1.13.3",
      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.3.tgz",
      "integrity": "sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA=="
    },
    "node_modules/util-deprecate": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
    }
  }
}
Original line number Diff line number Diff line
<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>UndoManager Tests</title>
	<link rel="stylesheet" href="resources/qunit.css">
</head>
<body>
	<div id="qunit"></div>
	<div id="qunit-fixture"></div>
	<script src="resources/jquery-1.9.1.js"></script>
	<script src="resources/underscore.js"></script>
	<script src="resources/backbone.js"></script>

	<script src="../Backbone.Undo.js"></script>

	<script src="resources/qunit.js"></script>
	<script src="Backbone.Undo.Tests.js"></script>
</body>
</html>
 No newline at end of file
Original line number Diff line number Diff line
test("Register and unregister", function () {
	var UndoManager = new Backbone.UndoManager;

	var model = new Backbone.Model,
	collection = new Backbone.Collection;

	UndoManager.register(model, collection);

	function getRegisteredObjects () {
		return UndoManager.objectRegistry.get();
	}

	strictEqual(getRegisteredObjects().length, 2, "Registering objects with the register method was successful");

	UndoManager.unregister(model);

	deepEqual(getRegisteredObjects(), [collection], "Unregistering an object with the unregister method was successful");

	UndoManager.register(model);
	UndoManager.unregisterAll();

	strictEqual(getRegisteredObjects().length, 0, "The unregisterAll function worked properly");

	var u1 = new Backbone.UndoManager({
		register: model
	}),
	u2 = new Backbone.UndoManager({
		register: collection
	}),
	u3 = new Backbone.UndoManager({
		register: [model, collection]
	});

	deepEqual(u1.objectRegistry.get(), [model], "Registering a single model over the 'register' attribute on instantiation was successful");
	deepEqual(u2.objectRegistry.get(), [collection], "Registering a single collection over the 'register' attribute on instantiation was successful");
	deepEqual(u3.objectRegistry.get(), [model, collection], "Registering multiple objects over the 'register' attribute on instantiation was successful");
})

test("Start and stop tracking", function () {
	var model = new Backbone.Model({
		"foo": "bar"
	})

	var collection = new Backbone.Collection([{"a": "b"}, {"c": "d"}]);

	var UndoManager = new Backbone.UndoManager({
		register: [model, collection]
	});

	var before = UndoManager.stack.length;

	model.set("hello", "world");
	collection.add({"e": "f"});

	strictEqual(UndoManager.stack.length, before, "Actions weren't added to the stack, because tracking hasn't started yet");

	ok(!UndoManager.isTracking(), "Tracking has not started yet");

	UndoManager.startTracking();

	ok(UndoManager.isTracking(), "Tracking has now started");

	model.set("hello", "you");
	collection.remove(collection.last());

	var after = UndoManager.stack.length;

	strictEqual(after, 2, "Two actions have been added to the stack, because tracking has started");

	UndoManager.stopTracking();

	model.set("hello", "kitty");
	collection.add({"e": "f"});
	model.set("hello", "you");
	collection.remove(collection.last());

	UndoManager.startTracking();

	strictEqual(UndoManager.stack.length, after, "No actions were added, because tracking was paused");

	UndoManager.unregisterAll();

	UndoManager = new Backbone.UndoManager({track: true});

	UndoManager.register(model, collection);

	model.set("hello", "hello");
	collection.add({"g": "h"});

	strictEqual(UndoManager.stack.length, 2, "{track:true} on instantiation started tracking at once");
});

test("Undo and redo Model-Changes", function () {
	var model = new Backbone.Model({
		"t": 1
	});

	var UndoManager = new Backbone.UndoManager({
		track: true,
		register: model
	});

	model.set("t", 2);

	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 1}, "Undoing changing a model attribute was successful");

	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2}, "Redoing changing a model attribute was successful");

	model.set("x", 1);

	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 2}, "Undoing setting a model attribute was successful");

	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2, "x": 1}, "Redoing setting a model attribute was successful");

	model.set({
		"y": 1,
		"x": 2
	})

	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 2, "x": 1}, "Undoing multiple changes at once was successful");

	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2, "x": 2, "y": 1}, "Redoing multiple changes at once was successful");

	UndoManager.undo();
	UndoManager.undo();
	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 1}, "Calling undo consecutively several times was successful");

	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 1}, "Additional undo calls that would be out of the stack's bounds were successfully ignored");

	UndoManager.redo();
	UndoManager.redo();
	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2, "x": 2, "y": 1}, "Calling redo consecutively several times was successful");

	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2, "x": 2, "y": 1}, "Additional redo calls that would be out of the stack's bounds were successfully ignored");	
})

test("Undo and redo Collection-changes", function () {
	var collection = new Backbone.Collection([{"t": 1}, {"t": 2}, {"t": 3}]),
	UndoManager = new Backbone.UndoManager({
		track: true,
		register: collection
	});

	collection.add({"t": 4});

	UndoManager.undo();

	deepEqual(collection.toJSON(), [{"t": 1}, {"t": 2}, {"t": 3}], "Adding a model to the collection was successfully undone");

	UndoManager.redo();

	deepEqual(collection.toJSON(), [{"t": 1}, {"t": 2}, {"t": 3}, {"t": 4}], "Adding a model to the collection was successfully redone");

	collection.remove(collection.first());

	UndoManager.undo();

	deepEqual(collection.toJSON(), [{"t": 1}, {"t": 2}, {"t": 3}, {"t": 4}], "Removing a model from the collection was successfully undone");

	UndoManager.redo();

	deepEqual(collection.toJSON(), [{"t": 2}, {"t": 3}, {"t": 4}], "Removing a model from the collection was successfully redone");

	collection.reset([{"a": 1}, {"a": 2}, {"a": 3}]);

	UndoManager.undo();

	deepEqual(collection.toJSON(), [{"t": 2}, {"t": 3}, {"t": 4}], "Resetting the collection was successfully undone");

	UndoManager.redo();

	deepEqual(collection.toJSON(), [{"a": 1}, {"a": 2}, {"a": 3}], "Resetting the collection was successfully redone");

	collection.first().destroy();

	UndoManager.undo();

	deepEqual(collection.toJSON(), [{"a": 1}, {"a": 2}, {"a": 3}], "Destroying a model in the collection was successfully undone");

	UndoManager.redo();

	deepEqual(collection.toJSON(), [{"a": 2}, {"a": 3}], "Destroying a model in the collection was successfully redone");
})

test("ObjectRegistry", function () {
	var model = new Backbone.Model,
	collection = new Backbone.Collection,
	nonBackboneObject = {"something":"else"},
	UndoManager = new Backbone.UndoManager,
	objectRegistry = UndoManager.objectRegistry;

	function compare (arr1, arr2) {
		return _.all(arr1, function (v) {
			return _.contains(arr2, v);
		});
	}

	objectRegistry.register(model);

	ok(objectRegistry.isRegistered(model), "The isRegistered method returns true");

	objectRegistry.register(collection);

	ok(compare(objectRegistry.get(), [model, collection]), "The get method rightfully returns a list of the registered objects");

	objectRegistry.register(model);
	objectRegistry.register(collection);

	equal(objectRegistry.get().length, 2, "Redundant registrations are correctly ignored");

	objectRegistry.register(nonBackboneObject);

	ok(compare(objectRegistry.get(), [model, collection, nonBackboneObject]), "Non-Backbone objects are also correctly registered");

	objectRegistry.unregister(model);
	objectRegistry.unregister(collection);
	objectRegistry.unregister(nonBackboneObject);

	equal(objectRegistry.get().length, 0, "Unregistering objects works properly");
})

test("Merging UndoManagers", 2, function () {
	var main = new Backbone.UndoManager({track:true}),
	special = new Backbone.UndoManager({track:true}),
	model1 = new Backbone.Model({
		"t": 1
	}),
	model2 = new Backbone.Model,
	obj = {
		object: model1,
		before: {"a": 1},
		after: {"b": 1}
	};

	main.id = main.stack.id = "main";
	special.id = special.stack.id = "special";
	
	special.changeUndoType("change", {
		"on": function () {
			return obj;
		}
	});

	special.register(model1);
	main.register(model2);

	main.merge(special);

	model1.set("t", 2); // Here we're triggering a change event

	// Now, the stack-length of main must have been changed
	deepEqual(main.stack.at(0).toJSON(), obj, "The action data was manipulated by the changed undotype")

	model2.set("t", 2); // Here we're checking if the main undoManager can still write on its stack

	deepEqual(main.stack.at(1).toJSON().after, {"t": 2}, "The main undomanager can still write on its own stack")
})

test("Clearing all actions", function () {
	var model = new Backbone.Model({
		"t": 1
	});

	var UndoManager = new Backbone.UndoManager({
		track: true,
		register: model
	});

	model.set("t", 2);
	model.set("t", 3);
	model.set("t", 4);

	UndoManager.clear();

	UndoManager.undo();

	deepEqual(model.toJSON(), {"t": 4}, "Clearing actions before undoing was successful");

	model.set("t", 2);
	model.set("t", 3);
	model.set("t", 4);

	UndoManager.undo();
	UndoManager.undo();

	UndoManager.clear();

	UndoManager.redo();

	deepEqual(model.toJSON(), {"t": 2}, "Clearing actions before redoing was successful");
})

test("Undoing all actions", function () {
	var model = new Backbone.Model({
		"t": 1
	});

	var UndoManager = new Backbone.UndoManager({
		track: true,
		register: model
	});

	model.set("t", 2);
	model.set("t", 3);
	model.set("t", 4);

	UndoManager.undoAll();

	deepEqual(model.toJSON(), {"t": 1}, "Calling undoAll was successful");

	UndoManager.redoAll();

	deepEqual(model.toJSON(), {"t": 4}, "Calling redoAll was successful");

	UndoManager.undo();
	UndoManager.undoAll();
	UndoManager.undo(true);

	deepEqual(model.toJSON(), {"t": 1}, "Mixing undoAll with undo doesn't cause any problems");
	
	UndoManager.undoAll(); // back to the stack's beginning

	UndoManager.redo();
	UndoManager.redoAll();
	UndoManager.redo(true);

	deepEqual(model.toJSON(), {"t": 4}, "Mixing redoAll with redo doesn't cause any problems");

	UndoManager.undoAll();
	UndoManager.undoAll();
	UndoManager.redoAll();
	UndoManager.redoAll();

	deepEqual(model.toJSON(), {"t": 4}, "Calling undoAll and redoAll multiple times doesn't cause any problems");

})

/**
 * Async tests for magic fusion
 */

var deferQueue = [];
function defer(fn) {
	var args = [].slice.call(arguments);
	deferQueue.push(fn, args);
}
function flushDeferQueue() {
	_.defer(function () {
		var fn = deferQueue.shift(),
			args = deferQueue.shift();
		if (fn) {
			fn.apply(null, args);
			flushDeferQueue();
		}
	})
}

asyncTest("Magic Fusion", 4, function () {
	var model = new Backbone.Model({
		"t": 1
	}), collection = new Backbone.Collection([{"a": 3}]),
	UndoManager = new Backbone.UndoManager({
		track: true,
		register: [model, collection]
	}), i;

	defer(function () {
		start();
		// Undo / Redo several changes
		for (i = 3; i < 10; i++) {
			model.set("t", i);
		}

		UndoManager.undo(true);

		equal(model.get("t"), 1, "Undoing all changes that happened on a model at once succeeded");

		UndoManager.redo(true);

		equal(model.get("t"), 9, "Redoing all changes that happened on a model at once succeeded");
	})

	defer(function () {
		stop();
		collection.add([{"a": 4}, {"a": 5}]);

		UndoManager.undo(true);

		equal(collection.length, 1, "Undoing adding several models to a collection succeeded");

		UndoManager.redo(true);

		equal(collection.length, 3, "Redoing adding several models to a collection succeeded");
		start();
	})

	flushDeferQueue();
})
Original line number Diff line number Diff line
/**
 * QUnit v1.11.0 - A JavaScript Unit Testing Framework
 *
 * http://qunitjs.com
 *
 * Copyright 2012 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

/** Font Family and Sizes */

#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}

#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }


/** Resets */

#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
	margin: 0;
	padding: 0;
}


/** Header */

#qunit-header {
	padding: 0.5em 0 0.5em 1em;

	color: #8699a4;
	background-color: #0d3349;

	font-size: 1.5em;
	line-height: 1em;
	font-weight: normal;

	border-radius: 5px 5px 0 0;
	-moz-border-radius: 5px 5px 0 0;
	-webkit-border-top-right-radius: 5px;
	-webkit-border-top-left-radius: 5px;
}

#qunit-header a {
	text-decoration: none;
	color: #c2ccd1;
}

#qunit-header a:hover,
#qunit-header a:focus {
	color: #fff;
}

#qunit-testrunner-toolbar label {
	display: inline-block;
	padding: 0 .5em 0 .1em;
}

#qunit-banner {
	height: 5px;
}

#qunit-testrunner-toolbar {
	padding: 0.5em 0 0.5em 2em;
	color: #5E740B;
	background-color: #eee;
	overflow: hidden;
}

#qunit-userAgent {
	padding: 0.5em 0 0.5em 2.5em;
	background-color: #2b81af;
	color: #fff;
	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}

#qunit-modulefilter-container {
	float: right;
}

/** Tests: Pass/Fail */

#qunit-tests {
	list-style-position: inside;
}

#qunit-tests li {
	padding: 0.4em 0.5em 0.4em 2.5em;
	border-bottom: 1px solid #fff;
	list-style-position: inside;
}

#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  {
	display: none;
}

#qunit-tests li strong {
	cursor: pointer;
}

#qunit-tests li a {
	padding: 0.5em;
	color: #c2ccd1;
	text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
	color: #000;
}

#qunit-tests li .runtime {
	float: right;
	font-size: smaller;
}

.qunit-assert-list {
	margin-top: 0.5em;
	padding: 0.5em;

	background-color: #fff;

	border-radius: 5px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
}

.qunit-collapsed {
	display: none;
}

#qunit-tests table {
	border-collapse: collapse;
	margin-top: .2em;
}

#qunit-tests th {
	text-align: right;
	vertical-align: top;
	padding: 0 .5em 0 0;
}

#qunit-tests td {
	vertical-align: top;
}

#qunit-tests pre {
	margin: 0;
	white-space: pre-wrap;
	word-wrap: break-word;
}

#qunit-tests del {
	background-color: #e0f2be;
	color: #374e0c;
	text-decoration: none;
}

#qunit-tests ins {
	background-color: #ffcaca;
	color: #500;
	text-decoration: none;
}

/*** Test Counts */

#qunit-tests b.counts                       { color: black; }
#qunit-tests b.passed                       { color: #5E740B; }
#qunit-tests b.failed                       { color: #710909; }

#qunit-tests li li {
	padding: 5px;
	background-color: #fff;
	border-bottom: none;
	list-style-position: inside;
}

/*** Passing Styles */

#qunit-tests li li.pass {
	color: #3c510c;
	background-color: #fff;
	border-left: 10px solid #C6E746;
}

#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name               { color: #366097; }

#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected           { color: #999999; }

#qunit-banner.qunit-pass                    { background-color: #C6E746; }

/*** Failing Styles */

#qunit-tests li li.fail {
	color: #710909;
	background-color: #fff;
	border-left: 10px solid #EE5757;
	white-space: pre;
}

#qunit-tests > li:last-child {
	border-radius: 0 0 5px 5px;
	-moz-border-radius: 0 0 5px 5px;
	-webkit-border-bottom-right-radius: 5px;
	-webkit-border-bottom-left-radius: 5px;
}

#qunit-tests .fail                          { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name             { color: #000000; }

#qunit-tests .fail .test-actual             { color: #EE5757; }
#qunit-tests .fail .test-expected           { color: green;   }

#qunit-banner.qunit-fail                    { background-color: #EE5757; }


/** Result */

#qunit-testresult {
	padding: 0.5em 0.5em 0.5em 2.5em;

	color: #2b81af;
	background-color: #D2E0E6;

	border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
	font-weight: bold;
}

/** Fixture */

#qunit-fixture {
	position: absolute;
	top: -10000px;
	left: -10000px;
	width: 1000px;
	height: 1000px;
}
Original line number Diff line number Diff line
{
  "name": "Backbone.Undo.js",
  "version": "0.2.5",
  "main": "Backbone.Undo.js",
  "license": "MIT license",
  "homepage": "http://backbone.undojs.com/",
  "authors": [
    "Oliver Sartun <osartun@gmail.com> (http://css3-html5.de)",
    "creynders <info@creynders.be> (http://www.creynders.be)",
    "Anton Shuvalov <anton_shuvalov@me.com> (http://anton-shuvalov.info)",
    "Marcin Lawrowski",
    "Vladislav Botvin <darkvlados@me.com>"
  ],
  "description": "A simple Backbone undo-manager for simple apps",
  "keywords": [
    "Backbone", "undo"
  ],
  "dependencies" :{
    "underscore": "1.4.4 - 1.8.3",
    "backbone": "1.0.0 - 1.2.1"
  },
  "ignore": [
    "Tests",
    ".gitignore",
    "sample",
    "README.md"
  ]
}