├── .gitignore
├── LICENSE
├── Readme.textile
├── docs
├── AC_OETags.js
├── all-classes.html
├── all-index-A.html
├── all-index-B.html
├── all-index-C.html
├── all-index-D.html
├── all-index-E.html
├── all-index-F.html
├── all-index-G.html
├── all-index-H.html
├── all-index-I.html
├── all-index-J.html
├── all-index-K.html
├── all-index-L.html
├── all-index-M.html
├── all-index-N.html
├── all-index-O.html
├── all-index-P.html
├── all-index-Q.html
├── all-index-R.html
├── all-index-S.html
├── all-index-T.html
├── all-index-U.html
├── all-index-V.html
├── all-index-W.html
├── all-index-X.html
├── all-index-Y.html
├── all-index-Z.html
├── asdoc.js
├── class-summary.html
├── cookies.js
├── help.js
├── images
│ ├── AirIcon12x12.gif
│ ├── P_AlternativeMetadataIndicator_30x28_N.png
│ ├── collapsed.gif
│ ├── detailHeaderRule.jpg
│ ├── detailSectionHeader.jpg
│ ├── expanded.gif
│ ├── inherit-arrow.gif
│ ├── inheritedSummary.gif
│ ├── logo.jpg
│ ├── titleTableBottom.jpg
│ ├── titleTableMiddle.jpg
│ └── titleTableTop.jpg
├── index-list.html
├── index.html
├── org
│ └── robotlegs
│ │ └── utilities
│ │ └── undoablecommand
│ │ ├── CommandEvent.html
│ │ ├── CommandHistory.html
│ │ ├── HistoryEvent.html
│ │ ├── UndoableCommand.html
│ │ ├── UndoableCommandBase.html
│ │ ├── class-list.html
│ │ ├── commands
│ │ ├── StepBackwardCommand.html
│ │ ├── StepForwardCommand.html
│ │ ├── class-list.html
│ │ └── package-detail.html
│ │ ├── interfaces
│ │ ├── IUndoableCommand.html
│ │ ├── class-list.html
│ │ └── package-detail.html
│ │ └── package-detail.html
├── override.css
├── package-frame.html
├── package-list.html
├── package-summary.html
├── print.css
├── style.css
└── title-bar.html
└── src
├── FlexUnitApplication.mxml
├── org
└── robotlegs
│ └── utilities
│ └── undoablecommand
│ ├── CommandEvent.as
│ ├── CommandHistory.as
│ ├── HistoryEvent.as
│ ├── UndoableCommand.as
│ ├── UndoableCommandBase.as
│ ├── commands
│ ├── StepBackwardCommand.as
│ └── StepForwardCommand.as
│ └── interfaces
│ └── IUndoableCommand.as
└── tests
├── MockUndoableCommand.as
├── MockUndoableCommandBase.as
├── TestHistory.as
├── TestHistoryEvents.as
├── TestManagedUndoableCommand.as
└── TestUndoableCommand.as
/.gitignore:
--------------------------------------------------------------------------------
1 | bin-debug
2 | html-template
3 | *.swf
4 | *.swp
5 | *.air
6 | .metadata
7 | .actionScriptProperties
8 | .flexProperties
9 | .flexLibProperties
10 | .metadata
11 | .project
12 | .settings*
13 | TEST-*
14 | bin
15 | .FlexUnitSettings
16 | *.orig
17 | report
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2009, 2010 the original author or authors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Readme.textile:
--------------------------------------------------------------------------------
1 | h1. Undoable Commands and History Controller for the Robotlegs Framework
2 |
3 | Undoable Commands aims to provide the most useful functions you'd need for undo/redo functionality in a gui app. I've successfully used in on a number of private projects.
4 |
5 | h2. Ddddddeprecated!
6 |
7 | Great news everyone! After years of inactivity (sadly, I no longer work on the Flash platform), `UndoableCommand` development continues courteousy of d3zza on his fork: "d3zza/robotlegs-utilities-UndoableCommand":https://github.com/d3zza/robotlegs-utilities-UndoableCommand. Check it out! Thanks d3zza!
8 |
9 | h2. Features
10 |
11 | * Keeps a linear history of executed Commands
12 | * Undo/Redo
13 | * Rewind/Fast Forward history
14 | * Rewrite history
15 | * Cancellable commands
16 | * Gets the ladies and/or gents
17 |
18 | h2. Important Classes
19 |
20 | h3. UndoableCommandBase
21 |
22 | Fairly barebones undoable command. You can use this with your own history controller or use the classes below to gain access to history control.
23 |
24 | h3. CommandHistory
25 |
26 | The history controller that provides the interface to move forward and backward through your command history.
27 |
28 | h3. UndoableCommand
29 |
30 | This command handles adding itself to a provided/injected CommandHistory object.
31 |
32 | _See the generated asdocs provided in the repository for more information_
33 |
34 | h2. Quickstart
35 |
36 | * Download the "latest release swc":http://github.com/secoif/robotlegs-utilities-UndoableCommand/downloads or feel free to download the source and compile for yourself, the test cases depend on FlexUnit4.
37 | * Include downloaded swc in your project
38 | * Create your command which extends UndoableCommand
39 | * Override the protected methods doExecute and undoExecute. doExecute should contain the code you want to execute when the command is fired. undoExecute should contain whatever code is needed to manually undo the actions performed by doExecute. The last line in both of these methods should be the call to super.doExecute()/undoExecute().
40 | * Put the following lines (+ necessary imports) into your Robotlegs context:
41 |
42 |
43 | // Create a CommandHistory to manages undo/redo history
44 | injector.mapSingleton(CommandHistory);
45 | // Events to trigger undo and redo
46 | commandMap.mapEvent(HistoryEvent.STEP_FORWARD, StepForwardCommand, HistoryEvent);
47 | commandMap.mapEvent(HistoryEvent.STEP_BACKWARD, StepBackwardCommand, HistoryEvent);
48 |
49 |
50 | * Map some event to the Command you created (just like you would with any normal robotlegs command)
51 | * Set a Button/Mediator to actually fire the HistoryEvent.STEP_FORWARD/BACKWARD events on the robotlegs event bus and you're done! :)
52 |
53 | h4. Cancelling a Command
54 |
55 | If you want to cancel a command you can call cancel() in your doExecute method and the command will not be added to the history stack (though you will have to ensure you don't actually make any changes to your data after you do this)
56 |
57 | h4. TODO
58 | Implement a minimal linked list variation based on UndoableCommandBase with no history controller
59 |
--------------------------------------------------------------------------------
/docs/AC_OETags.js:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // ADOBE SYSTEMS INCORPORATED
4 | // Copyright 2008 Adobe Systems Incorporated
5 | // All Rights Reserved.
6 | //
7 | // NOTICE: Adobe permits you to use, modify, and distribute this file
8 | // in accordance with the terms of the license agreement accompanying it.
9 | //
10 | ////////////////////////////////////////////////////////////////////////////////
11 |
12 |
13 | //v1.0
14 | function AC_AddExtension(src, ext)
15 | {
16 | if (src.indexOf('?') != -1)
17 | return src.replace(/\?/, ext+'?');
18 | else
19 | return src + ext;
20 | }
21 |
22 | function AC_Generateobj(objAttrs, params, embedAttrs)
23 | {
24 | var str = '';
28 | for (var i in params)
29 | str += ' ';
30 | str += ' ';
34 |
35 | document.write(str);
36 | }
37 |
38 | function AC_FL_RunContent(){
39 | var ret =
40 | AC_GetArgs
41 | ( arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
42 | , "application/x-shockwave-flash"
43 | );
44 | AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
45 | }
46 |
47 | function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
48 | var ret = new Object();
49 | ret.embedAttrs = new Object();
50 | ret.params = new Object();
51 | ret.objAttrs = new Object();
52 | for (var i=0; i < args.length; i=i+2){
53 | var currArg = args[i].toLowerCase();
54 |
55 | switch (currArg){
56 | case "classid":
57 | break;
58 | case "pluginspage":
59 | ret.embedAttrs[args[i]] = args[i+1];
60 | break;
61 | case "src":
62 | case "movie":
63 | args[i+1] = AC_AddExtension(args[i+1], ext);
64 | ret.embedAttrs["src"] = args[i+1];
65 | ret.params[srcParamName] = args[i+1];
66 | break;
67 | case "onafterupdate":
68 | case "onbeforeupdate":
69 | case "onblur":
70 | case "oncellchange":
71 | case "onclick":
72 | case "ondblClick":
73 | case "ondrag":
74 | case "ondragend":
75 | case "ondragenter":
76 | case "ondragleave":
77 | case "ondragover":
78 | case "ondrop":
79 | case "onfinish":
80 | case "onfocus":
81 | case "onhelp":
82 | case "onmousedown":
83 | case "onmouseup":
84 | case "onmouseover":
85 | case "onmousemove":
86 | case "onmouseout":
87 | case "onkeypress":
88 | case "onkeydown":
89 | case "onkeyup":
90 | case "onload":
91 | case "onlosecapture":
92 | case "onpropertychange":
93 | case "onreadystatechange":
94 | case "onrowsdelete":
95 | case "onrowenter":
96 | case "onrowexit":
97 | case "onrowsinserted":
98 | case "onstart":
99 | case "onscroll":
100 | case "onbeforeeditfocus":
101 | case "onactivate":
102 | case "onbeforedeactivate":
103 | case "ondeactivate":
104 | case "type":
105 | case "codebase":
106 | ret.objAttrs[args[i]] = args[i+1];
107 | break;
108 | case "width":
109 | case "height":
110 | case "align":
111 | case "vspace":
112 | case "hspace":
113 | case "class":
114 | case "title":
115 | case "accesskey":
116 | case "name":
117 | case "id":
118 | case "tabindex":
119 | ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
120 | break;
121 | default:
122 | ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
123 | }
124 | }
125 | ret.objAttrs["classid"] = classid;
126 | if (mimeType) ret.embedAttrs["type"] = mimeType;
127 | return ret;
128 | }
129 |
130 |
--------------------------------------------------------------------------------
/docs/all-classes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | All Classes - API Documentation
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/all-index-A.html:
--------------------------------------------------------------------------------
1 | A
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-B.html:
--------------------------------------------------------------------------------
1 | B
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-G.html:
--------------------------------------------------------------------------------
1 | G
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-J.html:
--------------------------------------------------------------------------------
1 | J
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-K.html:
--------------------------------------------------------------------------------
1 | K
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-L.html:
--------------------------------------------------------------------------------
1 | L
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-M.html:
--------------------------------------------------------------------------------
1 | M
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-Q.html:
--------------------------------------------------------------------------------
1 | Q
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-T.html:
--------------------------------------------------------------------------------
1 | T
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-V.html:
--------------------------------------------------------------------------------
1 | V
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-W.html:
--------------------------------------------------------------------------------
1 | W
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-X.html:
--------------------------------------------------------------------------------
1 | X
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-Y.html:
--------------------------------------------------------------------------------
1 | Y
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/all-index-Z.html:
--------------------------------------------------------------------------------
1 | Z
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/class-summary.html:
--------------------------------------------------------------------------------
1 | All Classes
6 | Documentation for classes includes syntax, usage information, and code samples for methods, properties, and event handlers and listeners for those APIs that belong to a specific class in ActionScript. The classes are listed alphabetically. If you are not sure to which class a certain method or property belongs, you can look it up in the Index.
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/cookies.js:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////////////////////////////////
2 | //
3 | // ADOBE SYSTEMS INCORPORATED
4 | // Copyright 2006-2008 Adobe Systems Incorporated
5 | // All Rights Reserved.
6 | //
7 | // NOTICE: Adobe permits you to use, modify, and distribute this file
8 | // in accordance with the terms of the license agreement accompanying it.
9 | //
10 | ////////////////////////////////////////////////////////////////////////////////
11 |
12 | /**
13 | * Read the JavaScript cookies tutorial at:
14 | * http://www.netspade.com/articles/javascript/cookies.xml
15 | */
16 |
17 | /**
18 | * Sets a Cookie with the given name and value.
19 | *
20 | * name Name of the cookie
21 | * value Value of the cookie
22 | * [expires] Expiration date of the cookie (default: end of current session)
23 | * [path] Path where the cookie is valid (default: path of calling document)
24 | * [domain] Domain where the cookie is valid
25 | * (default: domain of calling document)
26 | * [secure] Boolean value indicating if the cookie transmission requires a
27 | * secure transmission
28 | */
29 | function setCookie(name, value, expires, path, domain, secure)
30 | {
31 | document.cookie= name + "=" + escape(value) +
32 | ((expires) ? "; expires=" + expires.toGMTString() : "") +
33 | ((path) ? "; path=" + path : "") +
34 | ((domain) ? "; domain=" + domain : "") +
35 | ((secure) ? "; secure" : "");
36 | }
37 |
38 | /**
39 | * Gets the value of the specified cookie.
40 | *
41 | * name Name of the desired cookie.
42 | *
43 | * Returns a string containing value of specified cookie,
44 | * or null if cookie does not exist.
45 | */
46 | function getCookie(name)
47 | {
48 | var dc = document.cookie;
49 | var prefix = name + "=";
50 | var begin = dc.indexOf("; " + prefix);
51 | if (begin == -1)
52 | {
53 | begin = dc.indexOf(prefix);
54 | if (begin != 0) return null;
55 | }
56 | else
57 | {
58 | begin += 2;
59 | }
60 | var end = document.cookie.indexOf(";", begin);
61 | if (end == -1)
62 | {
63 | end = dc.length;
64 | }
65 | return unescape(dc.substring(begin + prefix.length, end));
66 | }
67 |
68 | /**
69 | * Deletes the specified cookie.
70 | *
71 | * name name of the cookie
72 | * [path] path of the cookie (must be same as path used to create cookie)
73 | * [domain] domain of the cookie (must be same as domain used to create cookie)
74 | */
75 | function deleteCookie(name, path, domain)
76 | {
77 | if (getCookie(name))
78 | {
79 | document.cookie = name + "=" +
80 | ((path) ? "; path=" + path : "") +
81 | ((domain) ? "; domain=" + domain : "") +
82 | "; expires=Thu, 01-Jan-70 00:00:01 GMT";
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/docs/images/AirIcon12x12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/AirIcon12x12.gif
--------------------------------------------------------------------------------
/docs/images/P_AlternativeMetadataIndicator_30x28_N.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/P_AlternativeMetadataIndicator_30x28_N.png
--------------------------------------------------------------------------------
/docs/images/collapsed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/collapsed.gif
--------------------------------------------------------------------------------
/docs/images/detailHeaderRule.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/detailHeaderRule.jpg
--------------------------------------------------------------------------------
/docs/images/detailSectionHeader.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/detailSectionHeader.jpg
--------------------------------------------------------------------------------
/docs/images/expanded.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/expanded.gif
--------------------------------------------------------------------------------
/docs/images/inherit-arrow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/inherit-arrow.gif
--------------------------------------------------------------------------------
/docs/images/inheritedSummary.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/inheritedSummary.gif
--------------------------------------------------------------------------------
/docs/images/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/logo.jpg
--------------------------------------------------------------------------------
/docs/images/titleTableBottom.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/titleTableBottom.jpg
--------------------------------------------------------------------------------
/docs/images/titleTableMiddle.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/titleTableMiddle.jpg
--------------------------------------------------------------------------------
/docs/images/titleTableTop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timoxley/robotlegs-utilities-UndoableCommand/5c220ac70cef2225fd4e1ca21f29fac148fef506/docs/images/titleTableTop.jpg
--------------------------------------------------------------------------------
/docs/index-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | API Documentation
4 |
5 |
6 |
7 |
8 |
9 | Index
10 |
11 |
12 |
17 |
18 | A
19 | N
20 |
21 |
22 | B
23 | O
24 |
25 |
26 | C
27 | P
28 |
29 |
30 | D
31 | Q
32 |
33 |
34 | E
35 | R
36 |
37 |
38 | F
39 | S
40 |
41 |
42 | G
43 | T
44 |
45 |
46 | H
47 | U
48 |
49 |
50 | I
51 | V
52 |
53 |
54 | J
55 | W
56 |
57 |
58 | K
59 | X
60 |
61 |
62 | L
63 | Y
64 |
65 |
66 | M
67 | Z
68 |
69 |
70 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | API Documentation
4 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Frame Alert
32 |
33 | This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
34 |
35 | Link to Non-frame version.
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/class-list.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand - API Documentation
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/commands/StepBackwardCommand.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.commands.StepBackwardCommand
6 |
14 | Map this command to the HistoryEvent.STEP_BACKWARD event to trigger an undo action.
15 | Provided for convenience.
16 |
Public Methods
Method Defined By
17 | Will undo a single command, if possible.
StepBackwardCommand
public var commandHistory:CommandHistory
public function execute():void
20 | Will undo a single command, if possible. Fails silently if not.
21 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/commands/StepForwardCommand.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.commands.StepForwardCommand
6 |
14 | Map this command to the HistoryEvent.STEP_FORWARD event to trigger a redo action.
15 | Provided for convenience.
16 |
Public Methods
Method Defined By
17 | Will redo a single command, if possible.
StepForwardCommand
public var commandHistory:CommandHistory
public function execute():void
20 | Will redo a single command, if possible. Fails silently if not.
21 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/commands/class-list.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.commands - API Documentation
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/commands/package-detail.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.commands Summary
6 | Classes
Class Description StepBackwardCommand
14 | Map this command to the HistoryEvent.STEP_BACKWARD event to trigger an undo action. StepForwardCommand
15 | Map this command to the HistoryEvent.STEP_FORWARD event to trigger a redo action.
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand
6 | Public Properties
Property Defined By eventDispatcher : IEventDispatcher
14 | Fire events when you execute or undo
15 |
IUndoableCommand
Public Methods
Method Defined By
16 | Perform an action
17 |
IUndoableCommand
18 | Reverse the performed action
19 |
IUndoableCommand
eventDispatcher:IEventDispatcher
22 | Fire events when you execute or undo
23 |
Implementation public function get eventDispatcher():IEventDispatcher
public function set eventDispatcher(value:IEventDispatcher):void
public function execute():void
24 | Perform an action
25 |
public function undo():void
26 | Reverse the performed action
27 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/interfaces/class-list.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.interfaces - API Documentation
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/interfaces/package-detail.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand.interfaces Summary
6 | Interfaces
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/org/robotlegs/utilities/undoablecommand/package-detail.html:
--------------------------------------------------------------------------------
1 | org.robotlegs.utilities.undoablecommand Summary
6 | Classes
Class Description CommandEvent
14 | CommandEvents are fired when single commands finish executing, finish undoing or are cancelled. CommandHistory
15 | Provides an interface to manage undo/redo history and fires events on the eventDispatcher
16 | when history events occur. HistoryEvent
17 | These events fire when operations occur on a CommandHistory object, or to control a CommandHistory object. UndoableCommand
18 | This command handles adding itself to the provided/injected CommandHistory. UndoableCommandBase
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/override.css:
--------------------------------------------------------------------------------
1 | /*
2 | ////////////////////////////////////////////////////////////////////////////////
3 | //
4 | // ADOBE SYSTEMS INCORPORATED
5 | // Copyright 2008 Adobe Systems Incorporated
6 | // All Rights Reserved.
7 | //
8 | // NOTICE: Adobe permits you to use, modify, and distribute this file
9 | // in accordance with the terms of the license agreement accompanying it.
10 | //
11 | ////////////////////////////////////////////////////////////////////////////////
12 | */
--------------------------------------------------------------------------------
/docs/package-frame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | API Documentation
4 |
5 |
6 |
7 |
8 |
9 |
10 | Frame Alert
11 | This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
12 |
13 | Link toNon-frame version.
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/docs/package-list.html:
--------------------------------------------------------------------------------
1 | Package List - API Documentation
--------------------------------------------------------------------------------
/docs/package-summary.html:
--------------------------------------------------------------------------------
1 | All Packages
6 |
Wed Aug 18 2010, 12:09 AM +10:00
--------------------------------------------------------------------------------
/docs/print.css:
--------------------------------------------------------------------------------
1 | /*
2 | ////////////////////////////////////////////////////////////////////////////////
3 | //
4 | // ADOBE SYSTEMS INCORPORATED
5 | // Copyright 2005-2008 Adobe Systems Incorporated
6 | // All Rights Reserved.
7 | //
8 | // NOTICE: Adobe permits you to use, modify, and distribute this file
9 | // in accordance with the terms of the license agreement accompanying it.
10 | //
11 | ////////////////////////////////////////////////////////////////////////////////
12 | */
13 |
14 | body {
15 | color: #000000;
16 | background: #ffffff;
17 | font-family: "Times New Roman", Times, serif;
18 | font-size: 12pt;
19 | }
20 | a {
21 | text-decoration: none;
22 | color: #000000;
23 | }
24 | pre {
25 | white-space: -moz-pre-wrap; /* Mozilla */
26 | white-space: -pre-wrap; /* Opera 4-6 */
27 | white-space: -o-pre-wrap; /* Opera 7 */
28 | word-wrap: break-word; /* IE */
29 | }
30 | .titleTableTopNav, .titleTableSubNav, .logoImage {
31 | display: none;
32 | }
33 | .packageFrame {
34 | display: none;
35 | }
36 | .titleTableSubTitle {
37 | font-weight: bold;
38 | }
39 | .classHeaderTableLabel {
40 | padding-right: 10px;
41 | vertical-align: top;
42 | }
43 | .showHideLinks {
44 | display: none;
45 | }
46 | html>body code {
47 | font-size: 10pt;
48 | }
49 | .summaryTableTitle, .detailSectionHeader {
50 | font-size: 14pt;
51 | font-weight: bold;
52 | padding-top: 15px;
53 | padding-bottom: 5px;
54 | }
55 | .summaryTable {
56 | border: 1px solid #000000;
57 | border-collapse: collapse;
58 | width: 100%;
59 | }
60 | .summaryTableDescription {
61 | padding-bottom: 20px;
62 | }
63 | .summaryTableSignatureCol, .summaryTableOwnerCol, .summaryTableLastCol, .summaryTableCol {
64 | border: 1px solid #000000;
65 | }
66 | .summaryTablePaddingCol {
67 | border: 1px solid #000000;
68 | border-right: 0px;
69 | }
70 | .summaryTableInheritanceCol, .summaryTableOperatorCol, .summaryTableStatementCol, .summaryTableSecondCol {
71 | border: 1px solid #000000;
72 | border-left: 0px;
73 | }
74 | .summaryTableLastCol {
75 | vertical-align: top;
76 | }
77 | .detailHeader {
78 | font-size: 13pt;
79 | padding-top: 100px;
80 | }
81 | .detailHeaderName {
82 | font-weight: bold;
83 | }
84 | .detailHeaderType {
85 | padding-left: 5px;
86 | }
87 | .detailHeaderRule {
88 | background: #FF0000;
89 | }
90 | .seeAlso {
91 | padding-bottom: 20px;
92 | margin-top: -20px;
93 | }
94 | .innertable {
95 | border-collapse: collapse;
96 | }
97 | .innertable td,.innertable th {
98 | border: 1px solid #000000;
99 | padding-left: 5px;
100 | padding-right: 5px;
101 | }
102 | .listing {
103 | font-size: 10pt;
104 | }
105 | .feedbackLink {
106 | display: none;
107 | }
108 | .copyright {
109 | font-size: 10pt;
110 | }
--------------------------------------------------------------------------------
/docs/title-bar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | API Documentation
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/FlexUnitApplication.mxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/CommandEvent.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand
2 | {
3 | import flash.events.Event;
4 |
5 | import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand;
6 |
7 | /**
8 | * CommandEvents are fired when single commands finish executing, finish undoing or are cancelled.
9 | *
10 | */
11 | public class CommandEvent extends Event
12 | {
13 | /**
14 | * CommandEvent of this type will be fired when a command has completed executing/redoing.
15 | */
16 | public static const EXECUTE_COMPLETE:String = "executeComplete";
17 |
18 | /**
19 | * CommandEvent of this type will be fired when a command has completed undoing.
20 | */
21 | public static const UNDO_EXECUTE_COMPLETE:String = "undoExecuteComplete";
22 |
23 | /**
24 | * CommandEvent of this type will be fired when execution of a command was cancelled.
25 | */
26 | public static const CANCELLED:String = "executeCancelled";
27 |
28 | /**
29 | * The command associated with the event.
30 | */
31 | public var command:IUndoableCommand;
32 |
33 | /**
34 | * @param type The type of event
35 | * @param command The command associated with the event
36 | */
37 | public function CommandEvent(type:String, command:IUndoableCommand) {
38 | super(type, false, false);
39 | this.command = command;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/CommandHistory.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand
2 | {
3 | import flash.events.IEventDispatcher;
4 |
5 | import org.robotlegs.utilities.undoablecommand.interfaces.*;
6 |
7 | /**
8 | * Provides an interface to manage undo/redo history and fires events on the eventDispatcher
9 | * when history events occur.
10 | */
11 | public class CommandHistory
12 | {
13 | /**
14 | * Command history data store.
15 | * Vector chosen over array for strong typing & speed
16 | */
17 | private var _historyStack:Vector.;
18 |
19 | /**
20 | * Pointer to the current command in the history stack.
21 | * First command starts at index 1.
22 | * If this is 0, we are pointing to no command (null) at the start of the stack
23 | */
24 | public var currentPosition:uint;
25 |
26 | /**
27 | * Supplied event bus to fire events upon.
28 | */
29 | [Inject]
30 | public var eventDispatcher:IEventDispatcher;
31 |
32 | public function CommandHistory() {
33 | _historyStack = new Vector.();
34 | currentPosition = 0;
35 | }
36 |
37 | /**
38 | * True if there's a command to redo.
39 | * @return true if there's a command to redo
40 | */
41 | public function get canStepForward():Boolean {
42 | return (currentPosition < numberOfHistoryItems);
43 | }
44 |
45 | /**
46 | * True if there's a command to undo.
47 | * @return true if there's a command to undo
48 | */
49 | public function get canStepBackward():Boolean {
50 | return (currentPosition > 0);
51 | }
52 |
53 | /**
54 | *
55 | * Redo/execute the next command on the history stack.
56 | * @return position in history stack after this operation
57 | */
58 | public function stepForward():uint {
59 | if (canStepForward) {
60 | _historyStack[currentPosition].execute();
61 | currentPosition++;
62 | }
63 | this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.STEP_FORWARD_COMPLETE, currentCommand));
64 |
65 | return currentPosition;
66 | }
67 |
68 | /**
69 | * Undo the previous command on the history stack and set the currentCommand to the previous command.
70 | * @return position in history stack after this operation
71 | */
72 | public function stepBackward():uint {
73 | var storeCurrentPosition:uint = currentPosition;
74 | if (canStepBackward) {
75 | // This is a hacky workaround:
76 | // If the undo was invoked from a CommandHistory object,
77 | // _historyStack[currentPosition - 1].undo() calls
78 | // stepBackward() again, in which case we want to prevent it from
79 | // updating the current position twice. I'm certain thar be a better way.
80 | // Update 6 months later: I have no idea what I'm talking about here.
81 | // I recall this whole business produced some fairly retarded program flow,
82 | // need to rethink design.
83 | _historyStack[currentPosition - 1].undo();
84 | currentPosition = storeCurrentPosition - 1;
85 | }
86 |
87 | // If there's no undone command,
88 | // dispatch null as the historyevent command
89 | var undoneCommand:IUndoableCommand;
90 | if (_historyStack.length > 0 && currentPosition != 0) {
91 | //the undone command
92 | undoneCommand = _historyStack[currentPosition];
93 | } else {
94 | undoneCommand = null;
95 | }
96 |
97 | this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.STEP_BACKWARD_COMPLETE, undoneCommand));
98 |
99 | return currentPosition;
100 | }
101 |
102 | /**
103 | * Undo all or some number of commands.
104 | * @param numTimes number of positions to move backward. The default, 0, rewinds to the start of the history (undoes all commands)
105 | * @return position in history stack after the rewind operation completes
106 | */
107 | public function rewind(numTimes:uint = 0):uint {
108 | var positionToMoveTo:uint;
109 |
110 | if (numTimes == 0) {
111 | positionToMoveTo = 0;
112 | } else {
113 | positionToMoveTo = currentPosition - numTimes;
114 | }
115 |
116 | // Move backward while possible
117 | while(canStepBackward && currentPosition != positionToMoveTo) {
118 | stepBackward();
119 | }
120 |
121 | this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.REWIND_COMPLETE, currentCommand));
122 | return currentPosition;
123 | }
124 |
125 | /**
126 | * Redo all or some number of commands.
127 | * @param numTimes number of positions to move forward.
128 | * The default, 0, fast forwards to the last item in the history (most recent).
129 | * @return position in history stack after the fastForward operation completes
130 | */
131 | public function fastForward(numTimes:uint = 0):uint {
132 | var positionToMoveTo:uint;
133 |
134 | if (numTimes == 0) {
135 | positionToMoveTo = numberOfHistoryItems;
136 | } else {
137 | positionToMoveTo = currentPosition + numTimes;
138 | }
139 |
140 | // Move forward
141 | while(canStepForward && currentPosition != positionToMoveTo) {
142 | stepForward();
143 | }
144 |
145 | this.eventDispatcher.dispatchEvent(new HistoryEvent(HistoryEvent.FAST_FORWARD_COMPLETE, currentCommand));
146 |
147 | return currentPosition;
148 | }
149 |
150 | /**
151 | * Total number of items in history, irrespective of their undone/redone state.
152 | * @return total number of items in history
153 | */
154 | public function get numberOfHistoryItems():uint {
155 | return _historyStack.length;
156 | }
157 |
158 | /**
159 | * Push a new command into the current position on the history stack and execute it.
160 | * If there are commands further forward in the history stack,
161 | * those commands are removed and this command becomes the
162 | * new top of the command stack.
163 | *
164 | * @return position in history stack after this operation
165 | */
166 | public function push(command:IUndoableCommand):uint {
167 |
168 | if (currentPosition != numberOfHistoryItems) {
169 | _historyStack = _historyStack.slice(0, currentPosition);
170 | }
171 |
172 | _historyStack.push(command);
173 |
174 | // Execute the command & move pointer forward
175 | stepForward();
176 | return currentPosition;
177 | }
178 |
179 | /**
180 | * Gets the command at the top of the history stack. This command will have already been executed.
181 | * @return command at the current position in the history stack,
182 | * or null if we're at position 0, in this case, there may be no commands on the stack at all.
183 | * @see currentPosition
184 | * @see numberOfHistoryItems
185 | */
186 | public function get currentCommand():IUndoableCommand {
187 | if (_historyStack.length == 0 || currentPosition == 0) {
188 | return null;
189 | }
190 | return _historyStack[currentPosition - 1];
191 | }
192 |
193 | /**
194 | * @private
195 | */
196 | public function toString():String {
197 | var output:String = "";
198 | var count:uint = 0;
199 | for each(var command:IUndoableCommand in _historyStack) {
200 | output += String(count) + String(command) + "\n";
201 | count++;
202 | }
203 | return output;
204 | }
205 | }
206 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/HistoryEvent.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand
2 | {
3 | import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand;
4 |
5 | /**
6 | * These events fire when operations occur on a CommandHistory object, or to control a CommandHistory object.
7 | */
8 | public class HistoryEvent extends CommandEvent
9 | {
10 | /**
11 | * You should Map this event to StepBackwardCommand to trigger an undo action.
12 | * Provided for convenience. Is not fired internally.
13 | *
14 | * // In your Robotlegs context:
15 | * commandMap.mapEvent(HistoryEvent.STEP_BACKWARD, StepBackwardCommand, HistoryEvent);
16 | *
17 | */
18 | public static const STEP_BACKWARD:String = "stepBackward";
19 |
20 | /**
21 | * You should map this event to a StepForwardCommand to trigger a redo action.
22 | * Provided for convenience. Is not fired internally.
23 | *
24 | * // In your Robotlegs context:
25 | * commandMap.mapEvent(HistoryEvent.STEP_FORWARD, StepForwardCommand, HistoryEvent);
26 | *
27 | */
28 | public static const STEP_FORWARD:String = "stepForward";
29 |
30 |
31 | /**
32 | * You should map this event to a command to trigger a rewind action.
33 | *
34 | * Provided for convenience. Is not fired internally.
35 | * TODO: Create a default RewindCommand
36 | */
37 | public static const REWIND:String = "rewind";
38 |
39 | /**
40 | * You should map this event to a command to trigger a fast-forward action.
41 | *
42 | * Provided for convenience. Is not fired internally.
43 | * TODO: Create a default FastForwardCommand
44 | */
45 | public static const FAST_FORWARD:String = "fastForward";
46 |
47 | /**
48 | * HistoryEvent with this type will be fired each time a command is executed/redone.
49 | * HistoryEvent with this type will be fired multiple times if performing a multi-step fast-forward.
50 | */
51 | public static const STEP_FORWARD_COMPLETE:String = "stepForwardComplete";
52 |
53 | /**
54 | * HistoryEvent with this type will be fired each time a command is undone.
55 | * HistoryEvent with this type will be fired multiple times if performing a multi-step rewind.
56 | */
57 | public static const STEP_BACKWARD_COMPLETE:String = "stepBackwardComplete";
58 |
59 |
60 |
61 | /**
62 | * HistoryEvent with this type will be fired when a rewind completes.
63 | */
64 | public static const REWIND_COMPLETE:String = "rewindComplete";
65 |
66 | /**
67 | * HistoryEvent with this type will be fired when a fast forward completes.
68 | */
69 | public static const FAST_FORWARD_COMPLETE:String = "fastForwardComplete";
70 |
71 | public function HistoryEvent(type:String, command:IUndoableCommand = null) {
72 | super(type, command);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/UndoableCommand.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand
2 | {
3 | import mx.messaging.messages.ErrorMessage;
4 |
5 | /**
6 | * This command handles adding itself to the provided/injected CommandHistory.
7 | *
8 | * UndoableCommands are pushed to the CommandHistory only once the Command has been executed,
9 | * and are removed once the Command has been undone. Undoable commands can be cancelled by calling cancel() from within
10 | * their doExecute() function, and they will not be added to the history.
11 | *
12 | * All functions assume the CommandHistory dependency has been provided as the public property 'history'.
13 | */
14 | public class UndoableCommand extends UndoableCommandBase
15 | {
16 |
17 | /**
18 | * @private
19 | * Flag true after this Command has been pushed to CommandHistory
20 | */
21 | private var hasRegisteredWithHistory:Boolean;
22 |
23 | /**
24 | * @private
25 | * Flag true after this Command has been stepped back by CommandHistory
26 | */
27 | private var hasSteppedBack:Boolean;
28 |
29 | protected var isCancelled:Boolean = false;
30 |
31 | /**
32 | * Reference to the CommandHistory being used by this Command
33 | */
34 | [Inject]
35 | public var history:CommandHistory;
36 |
37 | /**
38 | * @inheritDoc
39 | */
40 | public function UndoableCommand(doFunction:Function = null, undoFunction:Function = null) {
41 | super(doFunction, undoFunction);
42 | }
43 |
44 | /**
45 | * Call this function in your doExecute method to prevent this item from being added to the history stack.
46 | * TODO: Throw an error if cancelling within a redo. Currently assumes if the cancel condition did not fire
47 | * on the first run, they will pass on subsequent executions. This is faulty.
48 | */
49 | public function cancel():void {
50 | isCancelled = true;
51 | }
52 |
53 | /**
54 | * Executes the command.
55 | * Override this function in your subclasses to implement your command's actions.
56 | * You may call cancel() at any point in this function to prevent it from being added
57 | * to the history stack, but remember that execution does not stop when you call cancel and
58 | * you will need to ensure the doExecute method did not actually make any changes.
59 | *
60 | * Ensure you call super.doExecute() at the end of your subclassed method.
61 | * Note command is only automatically pushed to history once we try to execute this command
62 | * @inheritDoc
63 | * @see undoExecute
64 | */
65 | override protected function doExecute():void {
66 | // Only push to history once we actually try to execute this command
67 | if (!hasRegisteredWithHistory && !isCancelled) {
68 | hasRegisteredWithHistory = true;
69 | hasExecuted = true;
70 | history.push(this);
71 | }
72 | super.doExecute();
73 | }
74 |
75 | /**
76 | * Override this function in your subclasses to implement the undo of the actions performed in doExecute(). Ensure you call super.undoExecute() at the end of your subclassed method.
77 | * @inheritDoc
78 | * @see doExecute
79 | * @throws Error Prevents history corruption by throwing error if trying to undo this command and it's not at the top of the history (i.e. next to be undone).
80 | */
81 | override protected function undoExecute():void {
82 | if (hasExecuted) {
83 | this.hasExecuted = false;
84 |
85 | if (!hasSteppedBack) {
86 | hasSteppedBack = true;
87 | if (history.currentCommand != this) {
88 | throw new Error("Cannot undo command unless this command is first in command history!");
89 | }
90 |
91 | history.stepBackward();
92 | hasSteppedBack = false;
93 | }
94 |
95 | }
96 | if (isCancelled) {
97 | throw new Error("Trying to undo a cancelled command!");
98 | }
99 | super.undoExecute();
100 | }
101 |
102 | /**
103 | * @private
104 | * Checks if this command has added itself to the command history.
105 | * Ensures we don't let this Command push to the CommandHistory more than once.
106 | */
107 | private function registerIfRequired():void {
108 | if (!hasRegisteredWithHistory) {
109 | hasRegisteredWithHistory = true;
110 | history.push(this);
111 | }
112 | }
113 | }
114 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/UndoableCommandBase.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand
2 | {
3 | import flash.events.EventDispatcher;
4 | import flash.events.IEventDispatcher;
5 |
6 | import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand;
7 |
8 | public class UndoableCommandBase implements IUndoableCommand
9 | {
10 | /**
11 | * Keeps track of whether this command has been executed,
12 | * to prevent undoing commands that have not been yet been executed.
13 | */
14 | protected var hasExecuted:Boolean;
15 |
16 | /**
17 | * @private
18 | * Reference to the function to execute in the execute() function
19 | */
20 | private var doFunction:Function;
21 |
22 | /**
23 | * @private
24 | * Reference to the undo function to execute in the undo() function
25 | */
26 | private var undoFunction:Function;
27 |
28 | private var _eventDispatcher:IEventDispatcher;
29 |
30 | private static const EXECUTE:String = "doExecuteCommand";
31 |
32 | /**
33 | * Creates a new UndoableCommand
34 | * @param doFunction the function to execute
35 | * @param undoFunction execute this function to undo the operations of doFunction
36 | * @param autoExecute automatically executes this command on creation. Be careful when setting this false
37 | */
38 | public function UndoableCommandBase(doFunction:Function = null, undoFunction:Function = null) {
39 | // set function defaults
40 | if (doFunction is Function) {
41 | this.doFunction = doFunction;
42 | } else {
43 | this.doFunction = doExecute;
44 | }
45 |
46 | if (undoFunction is Function) {
47 | this.undoFunction = undoFunction;
48 | } else {
49 | this.undoFunction = undoExecute;
50 | }
51 | }
52 |
53 | /**
54 | * Executes the command.
55 | * If we passed in an execute function to the constructor,
56 | * execute passed-in function, otherwise execute overriden
57 | * doExecute function.
58 | * Will not execute more than once without first undoing
59 | */
60 | public final function execute():void {
61 | if (!hasExecuted) {
62 | doFunction();
63 | hasExecuted = true;
64 | eventDispatcher.dispatchEvent(new CommandEvent(CommandEvent.EXECUTE_COMPLETE, this));
65 | }
66 | }
67 |
68 | /**
69 | * Executes the undo function.
70 | * If we passed in an undo function to the constructor,
71 | * execute passed-in undo function, otherwise execute overriden
72 | * undoExecute function.
73 | * Will not undo if function has not executed.
74 | */
75 | public final function undo():void {
76 | if (hasExecuted) {
77 | undoFunction();
78 | hasExecuted = false;
79 | eventDispatcher.dispatchEvent(new CommandEvent(CommandEvent.UNDO_EXECUTE_COMPLETE, this));
80 | }
81 | }
82 |
83 | /**
84 | * Subclasses MUST override this function with the body of their command.
85 | */
86 | protected function doExecute():void {
87 |
88 | }
89 |
90 | /**
91 | * Subclasses must override this function. This function should undo whatever the doExecute command did.
92 | */
93 | protected function undoExecute():void {
94 |
95 | }
96 |
97 | /**
98 | * Event bus to dispatch events on.
99 | */
100 | [Inject]
101 | public function get eventDispatcher():IEventDispatcher {
102 | return _eventDispatcher;
103 | }
104 |
105 | public function set eventDispatcher(value:IEventDispatcher):void {
106 | _eventDispatcher = value;
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/commands/StepBackwardCommand.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand.commands
2 | {
3 | import org.robotlegs.utilities.undoablecommand.CommandHistory;
4 |
5 |
6 | /**
7 | * Map this command to the HistoryEvent.STEP_BACKWARD event to trigger an undo action.
8 | * Provided for convenience.
9 | */
10 | public class StepBackwardCommand
11 | {
12 | [Inject]
13 | public var commandHistory:CommandHistory;
14 |
15 | /**
16 | * Will undo a single command, if possible. Fails silently if not.
17 | */
18 | public function execute():void {
19 | commandHistory.stepBackward();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/commands/StepForwardCommand.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand.commands
2 | {
3 | import org.robotlegs.utilities.undoablecommand.CommandHistory;
4 |
5 | /**
6 | * Map this command to the HistoryEvent.STEP_FORWARD event to trigger a redo action.
7 | * Provided for convenience.
8 | */
9 | public class StepForwardCommand
10 | {
11 | [Inject]
12 | public var commandHistory:CommandHistory;
13 |
14 | /**
15 | * Will redo a single command, if possible. Fails silently if not.
16 | */
17 | public function execute():void {
18 | commandHistory.stepForward();
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/org/robotlegs/utilities/undoablecommand/interfaces/IUndoableCommand.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.utilities.undoablecommand.interfaces
2 | {
3 | import flash.events.IEventDispatcher;
4 |
5 | public interface IUndoableCommand {
6 |
7 | /**
8 | * Perform an action
9 | */
10 | function execute():void;
11 |
12 | /**
13 | * Reverse the performed action
14 | */
15 | function undo():void;
16 |
17 | /**
18 | * Fire events when you execute or undo
19 | */
20 | function get eventDispatcher():IEventDispatcher;
21 | function set eventDispatcher(value:IEventDispatcher):void;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/tests/MockUndoableCommand.as:
--------------------------------------------------------------------------------
1 | package tests {
2 | import org.robotlegs.utilities.undoablecommand.UndoableCommand;
3 |
4 |
5 | /**
6 | * @private
7 | */
8 | public class MockUndoableCommand extends UndoableCommand {
9 |
10 | /**
11 | * Reference to some array to be tested on.
12 | */
13 | public var testArray:Array;
14 | public var shouldCancel:Boolean = false;
15 |
16 | /**
17 | * Cause some change to the array
18 | */
19 | override protected function doExecute():void {
20 | if (shouldCancel) {
21 | cancel();
22 | return;
23 | }
24 | this.testArray.push(new Object());
25 | super.doExecute();
26 | }
27 |
28 | /**
29 | * Undo the change on the array
30 | */
31 | override protected function undoExecute():void {
32 | // pop() isn't the best undo is it?
33 | // will do for now.
34 | this.testArray.pop();
35 | super.undoExecute();
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/tests/MockUndoableCommandBase.as:
--------------------------------------------------------------------------------
1 | package tests
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | import org.robotlegs.utilities.undoablecommand.UndoableCommandBase;
6 | import org.robotlegs.utilities.undoablecommand.interfaces.IUndoableCommand;
7 |
8 | /**
9 | * @private
10 | */
11 | public class MockUndoableCommandBase extends UndoableCommandBase implements IUndoableCommand
12 | {
13 | public static var testArray:Array = new Array();
14 |
15 | public function MockUndoableCommandBase() {
16 | eventDispatcher = new EventDispatcher();
17 | super();
18 | }
19 |
20 | /**
21 | * Cause damage to the array
22 | */
23 | override protected function doExecute():void {
24 |
25 | trace("Do ta:" + testArray);
26 | testArray.push(new Object());
27 | super.doExecute();
28 | }
29 |
30 | /**
31 | * Undo the damage on the array
32 | */
33 | override protected function undoExecute():void {
34 | trace("Undo ta:" + testArray);
35 | testArray.pop();
36 | super.undoExecute();
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/tests/TestHistoryEvents.as:
--------------------------------------------------------------------------------
1 | package tests
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | import flexunit.framework.Assert;
6 |
7 | import org.flexunit.async.Async;
8 | import org.robotlegs.utilities.undoablecommand.CommandHistory;
9 | import org.robotlegs.utilities.undoablecommand.HistoryEvent;
10 |
11 | public class TestHistoryEvents
12 | {
13 | // Reference declaration for class to test
14 | private var history:CommandHistory;
15 | private var eventBus:EventDispatcher;
16 | private var bananaCommand:MockUndoableCommandBase;
17 | private var appleCommand:MockUndoableCommandBase;
18 | private var pineappleCommand:MockUndoableCommandBase;
19 |
20 | public function TestHistoryEvents()
21 | {
22 | }
23 |
24 | [Before]
25 | public function setupTests():void {
26 | history = new CommandHistory();
27 | eventBus = new EventDispatcher()
28 | history.eventDispatcher = eventBus;
29 | MockUndoableCommandBase.testArray = new Array()
30 | bananaCommand = new MockUndoableCommandBase();
31 | bananaCommand.eventDispatcher = eventBus;
32 | appleCommand = new MockUndoableCommandBase();
33 | appleCommand.eventDispatcher = eventBus;
34 | pineappleCommand = new MockUndoableCommandBase();
35 | pineappleCommand.eventDispatcher = eventBus;
36 | }
37 |
38 | [After]
39 | public function reset():void {
40 | history = null;
41 | eventBus = null;
42 | MockUndoableCommandBase.testArray = null;
43 | }
44 |
45 |
46 | [Test(async)]
47 | public function testFastForwardEvents():void {
48 | Async.handleEvent(this, history.eventDispatcher, HistoryEvent.FAST_FORWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void {
49 | Assert.assertStrictlyEquals(pineappleCommand, event.command)
50 | });
51 |
52 | history.push(bananaCommand);
53 | history.push(appleCommand);
54 | history.push(pineappleCommand);
55 | history.rewind();
56 | history.fastForward();
57 | }
58 |
59 | [Test(async)]
60 | public function testRewindEvents():void {
61 | Async.handleEvent(this, history.eventDispatcher, HistoryEvent.REWIND_COMPLETE, function(event:HistoryEvent, passThrough:Object):void {
62 | Assert.assertNull(event.command);
63 |
64 | });
65 |
66 | history.push(bananaCommand);
67 | history.push(appleCommand);
68 | history.push(pineappleCommand);
69 | history.rewind();
70 | }
71 |
72 | [Test(async)]
73 | public function testStepBackwardEvents():void {
74 | Async.handleEvent(this, history.eventDispatcher, HistoryEvent.STEP_BACKWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void {
75 | Assert.assertStrictlyEquals(pineappleCommand, event.command);
76 | });
77 |
78 | history.push(bananaCommand);
79 | history.push(appleCommand);
80 | history.push(pineappleCommand);
81 | history.stepBackward();
82 | }
83 |
84 | [Test(async)]
85 | public function testStepForwardEvents():void
86 | {
87 | Async.handleEvent(this, history.eventDispatcher, HistoryEvent.STEP_FORWARD_COMPLETE, function(event:HistoryEvent, passThrough:Object):void {
88 | Assert.assertStrictlyEquals(bananaCommand, event.command);
89 |
90 | });
91 |
92 | history.push(bananaCommand);
93 | }
94 | }
95 | }
--------------------------------------------------------------------------------
/src/tests/TestManagedUndoableCommand.as:
--------------------------------------------------------------------------------
1 | package tests
2 | {
3 | import flash.display.DisplayObjectContainer;
4 | import flash.events.EventDispatcher;
5 |
6 | import flexunit.framework.Assert;
7 |
8 | import org.robotlegs.utilities.undoablecommand.CommandHistory;
9 |
10 |
11 | /**
12 | * @private
13 | */
14 | public class TestManagedUndoableCommand
15 | {
16 | // Reference declaration for class to test
17 | private var _managedUndoableCommand:MockUndoableCommand;
18 |
19 | public function TestManagedUndoableCommand()
20 | {
21 | }
22 |
23 | private var testArray:Array;
24 | private var contextView:DisplayObjectContainer;
25 |
26 | private var history:CommandHistory;
27 | private var eventBus:EventDispatcher;
28 |
29 | [Before]
30 | public function setupTests():void {
31 | eventBus = new EventDispatcher();
32 | testArray = new Array();
33 | history = new CommandHistory();
34 | history.eventDispatcher = eventBus;
35 | _managedUndoableCommand = createCommand();
36 | }
37 |
38 | private function createCommand():MockUndoableCommand {
39 | var newCommand:MockUndoableCommand = new MockUndoableCommand();
40 | newCommand.testArray = testArray;
41 | newCommand.history = history;
42 | newCommand.eventDispatcher = eventBus;
43 |
44 | return newCommand;
45 | }
46 |
47 | [After]
48 | public function reset():void {
49 | testArray = null;
50 | _managedUndoableCommand = null;
51 | history = null;
52 | }
53 |
54 | [Test]
55 | public function testInitialisation():void {
56 | Assert.assertNotNull(_managedUndoableCommand);
57 | Assert.assertNotNull(testArray);
58 | }
59 |
60 | [Test]
61 | public function testExecute():void {
62 | Assert.assertEquals(0, testArray.length);
63 | _managedUndoableCommand.execute();
64 | Assert.assertEquals(1, testArray.length);
65 | }
66 |
67 | [Test]
68 | public function testUndo():void {
69 | _managedUndoableCommand.execute();
70 | Assert.assertEquals(1, testArray.length);
71 | _managedUndoableCommand.undo();
72 | Assert.assertEquals(0, testArray.length);
73 | }
74 |
75 | [Test]
76 | public function testExecuteMultiple():void {
77 | _managedUndoableCommand.execute();
78 | _managedUndoableCommand.execute();
79 | _managedUndoableCommand.execute();
80 | Assert.assertEquals(1, testArray.length);
81 |
82 | }
83 |
84 | [Test]
85 | public function testUndoMultiple():void {
86 | _managedUndoableCommand.execute();
87 | Assert.assertEquals(1, testArray.length);
88 | _managedUndoableCommand.undo();
89 | _managedUndoableCommand.undo();
90 | _managedUndoableCommand.undo();
91 | Assert.assertEquals(0, testArray.length);
92 | }
93 |
94 | [Test]
95 | public function testUndoNothingToUndo():void {
96 | _managedUndoableCommand.undo();
97 | Assert.assertEquals(0, testArray.length);
98 | }
99 |
100 | [Test]
101 | public function testDefaultFunctions():void {
102 | var command:MockUndoableCommand = createCommand();/// = new MockManagedUndoableCommand();
103 |
104 | command.testArray = testArray;
105 | command.history = history;
106 | command.execute();
107 | Assert.assertEquals(1, testArray.length);
108 | command.undo();
109 | Assert.assertEquals(0, testArray.length);
110 | }
111 |
112 | [Test]
113 | // Test forward/back/position settings while
114 | // moving backwards & forwards
115 | public function testGetCurrentCommand():void {
116 | Assert.assertNull(history.currentCommand);
117 | Assert.assertEquals(0, history.currentPosition);
118 |
119 | var appleCommand:MockUndoableCommand = createCommand();
120 |
121 | var bananaCommand:MockUndoableCommand = createCommand();
122 |
123 | var pineappleCommand:MockUndoableCommand = createCommand();
124 |
125 | /*_testHistory.push(appleCommand);
126 | _testHistory.push(bananaCommand);
127 | _testHistory.push(pineappleCommand);*/
128 |
129 | appleCommand.execute();
130 | bananaCommand.execute();
131 | pineappleCommand.execute();
132 |
133 | Assert.assertEquals(pineappleCommand, history.currentCommand);
134 | history.stepBackward();
135 | Assert.assertEquals(bananaCommand, history.currentCommand);
136 | history.stepForward();
137 | Assert.assertEquals(pineappleCommand, history.currentCommand);
138 | history.stepBackward();
139 | history.stepBackward();
140 | Assert.assertEquals(appleCommand, history.currentCommand);
141 | }
142 |
143 | [Test]
144 | // Test forward/back/position settings while
145 | // moving backwards & forwards
146 | public function testAddToHistory():void {
147 | var appleCommand:MockUndoableCommand = createCommand();
148 | Assert.assertNull(history.currentCommand);
149 | appleCommand.execute();
150 | Assert.assertEquals(appleCommand, history.currentCommand);
151 | Assert.assertEquals(1, testArray.length);
152 | }
153 |
154 | [Test]
155 | // Test forward/back/position settings while
156 | // moving backwards & forwards
157 | public function testCommandUndo():void {
158 | var appleCommand:MockUndoableCommand = createCommand();
159 | Assert.assertNull(history.currentCommand);
160 | appleCommand.execute();
161 |
162 | Assert.assertEquals(appleCommand, history.currentCommand);
163 | Assert.assertEquals(1, testArray.length);
164 | appleCommand.undo();
165 | Assert.assertNull(history.currentCommand);
166 | Assert.assertEquals(0, testArray.length);
167 |
168 | }
169 |
170 | [Test]
171 | // Test forward/back/position settings while
172 | // moving backwards & forwards
173 | public function testHistoryUndo():void {
174 | var appleCommand:MockUndoableCommand = createCommand();
175 | Assert.assertNull(history.currentCommand);
176 | appleCommand.execute();
177 | Assert.assertEquals(1, testArray.length);
178 | Assert.assertEquals(appleCommand, history.currentCommand);
179 | Assert.assertEquals(1, history.currentPosition);
180 | history.stepBackward();
181 | Assert.assertEquals(0, history.currentPosition);
182 | Assert.assertNull(history.currentCommand);
183 | Assert.assertEquals(0, testArray.length);
184 | history.stepForward();
185 | Assert.assertEquals(1, testArray.length);
186 | Assert.assertEquals(appleCommand, history.currentCommand);
187 | Assert.assertEquals(1, history.currentPosition);
188 | }
189 |
190 | [Test]
191 | // Test cancelling the command
192 | public function testCancellingDoesNothing():void {
193 | var appleCommand:MockUndoableCommand = createCommand();
194 | appleCommand.shouldCancel = true;
195 | appleCommand.execute();
196 | //Make sure nothing actually happened
197 | Assert.assertEquals(0, history.currentPosition);
198 | Assert.assertNull(history.currentCommand);
199 | Assert.assertEquals(0, testArray.length);
200 |
201 | }
202 |
203 | [Test(expects="Error")]
204 | // Test trying to undo a cancelled command throws an error
205 | public function testUndoingACancelledCommandErrors():void {
206 | var appleCommand:MockUndoableCommand = createCommand();
207 | appleCommand.shouldCancel = true;
208 | appleCommand.execute();
209 | appleCommand.undo();
210 | }
211 | }
212 | }
--------------------------------------------------------------------------------
/src/tests/TestUndoableCommand.as:
--------------------------------------------------------------------------------
1 | package tests
2 | {
3 | import flash.events.EventDispatcher;
4 |
5 | import flexunit.framework.Assert;
6 |
7 | import org.robotlegs.utilities.undoablecommand.UndoableCommandBase;
8 |
9 | /**
10 | * @private
11 | */
12 | public class TestUndoableCommand
13 | {
14 | // Reference declaration for class to test
15 | private var _undoableCommand:UndoableCommandBase;
16 |
17 | private var testArray:Array;
18 | private var testObject:Object;
19 | private var eventBus:EventDispatcher;
20 |
21 | private function doStuff():void {
22 | testArray.push(testObject);
23 | }
24 |
25 | private function undoStuff():void {
26 | testArray.pop();
27 | }
28 |
29 | [Before]
30 | public function setupTests():void {
31 | testArray = new Array();
32 | eventBus = new EventDispatcher();
33 | _undoableCommand = new UndoableCommandBase(doStuff, undoStuff);
34 | _undoableCommand.eventDispatcher = eventBus;
35 | }
36 |
37 | [After]
38 | public function reset():void {
39 | _undoableCommand = null;
40 | testArray = null;
41 | eventBus = null;
42 | }
43 |
44 | [Test]
45 | public function testInitialisation():void {
46 | Assert.assertNotNull(_undoableCommand);
47 | Assert.assertNotNull(testArray);
48 | }
49 |
50 | [Test]
51 | public function testExecute():void {
52 | _undoableCommand.execute();
53 | Assert.assertEquals(testArray.length, 1);
54 | }
55 |
56 | [Test]
57 | public function testUndo():void {
58 | _undoableCommand.execute();
59 | Assert.assertEquals(testArray.length, 1);
60 | _undoableCommand.undo();
61 | Assert.assertEquals(testArray.length, 0);
62 | }
63 |
64 | [Test]
65 | public function testExecuteMultiple():void {
66 | _undoableCommand.execute();
67 | _undoableCommand.execute();
68 | _undoableCommand.execute();
69 | Assert.assertEquals(testArray.length, 1);
70 | }
71 |
72 | [Test]
73 | public function testUndoMultiple():void {
74 | _undoableCommand.execute();
75 | _undoableCommand.undo();
76 | _undoableCommand.undo();
77 | _undoableCommand.undo();
78 | Assert.assertEquals(testArray.length, 0);
79 | }
80 |
81 | [Test]
82 | public function testUndoNothingToUndo():void {
83 | _undoableCommand.undo();
84 | Assert.assertEquals(testArray.length, 0);
85 | }
86 |
87 | [Test]
88 | public function testDefaultFunctions():void {
89 | var command:MockUndoableCommandBase = new MockUndoableCommandBase();
90 | MockUndoableCommandBase.testArray = testArray;
91 | command.execute();
92 | Assert.assertEquals(testArray.length, 1);
93 | command.undo();
94 | Assert.assertEquals(testArray.length, 0);
95 | }
96 |
97 | }
98 | }
--------------------------------------------------------------------------------