hndout8

advertisement
JavaScript
• Original name: Mocha/LiveScript for Netscape Browsers
– By Brendon Eich; Renamed JavaScript in 1995
– Agreement: Netscape to support Java; Sun allow name change
• Versions
– JavaScript: Copyright owned by Sun Microsystems, now Oracle
– Jscript: Microsoft’s version, named to avoid trademark issues
– JavaScript submitted by Netscape to the European Computer
Manufacturers Association (ECMA) in 1996 for standardization,
and ECMAScript was born
– ActionScript created by MacroMedia for Flash. The current
version is compliant with the EMAScript standard
• Notes
– JavaScript, JScript, ActionScript are supersets of ECMAScript
– http://www.ecma-international.org/publications/standards/Ecma-262.htm
JavaScript
• Original design and purpose
–
–
–
–
Add life to web pages (leading to the name, LiveScript)
User Web-content interactivity
HTML manipulation with the Document Object Model (DOM)
Client-side interpreted scripting language living in a browser
• Extended design and purpose
–
–
–
–
–
–
Client side/server interaction using AJAX (Asynchronous Java and XML) calls
JavaScript's use in PDF documents
Enhance the user experience
Desktop widgets (small application showing on the desktop)
Mobile applications
Server side scripting
• Recent activity contributing to its popularity
–
–
–
–
Libraries, frameworks, and improved efficiency
Language enhancements (Device control, local storage)
Powerful cross platform programming model
Minimize the need for browser plugins
Difficulties
• Browser compatibility
–
–
–
–
Test on many browsers to ensure that the application works
Each browser renders the application differently
Desktop browsers: Firefox, Explorer, Chrome, Opera, Safari, Konquerer
Mobile Device Browsers: Some WebKit-based, others are derivatives of
their desktop counterparts, and some are unique to a particular device
– JavaScript code blocks everything that is happening in the browser, so
performance is a serious issue
• Compiling
–
–
–
–
Java is interpreted, so there is no actual compiler
An Eclipse plug-in displays errors, sometimes incorrectly
There are “lint” utilities available, with ideosyncrocies (i++)
Browser utilities: Most browsers support console.log with a show
console option that also displays run-time syntactical errors.
Debugging JavaScript
• Debugging
– Desktop browsers have reasonable debuggers available
– Mobile debuggers don’t always include debuggers
•
•
•
•
•
Firefox: firebug is a free downloadable plug in
Explorer: The Developer tools option brings up a debugging window
Chrome: Developer tools is an option under the tools option
Opera: The inspect element option launches a debugging window
Safari: Preferences/advanced must be first set to show the developer
option in the options menu. After this, the start debugging option can be
selected.
Browser incompatibilities and lack of a universal API is one
reason why developers cringe at the thought of JavaScript
Using JavaScript in Web-pages
• Within the page itself (preferably between the
<head> and </head> tags
<head><title>Some Page</title>
<script type="text/javascript">
/* Code here */
</script>
• In an external text file
<head><title>Some Page</title>
<script type="text/javascript" src="foo.js"></script>
• Within an HTML tag
<body onload="callSomeFunction();">
Note: JavaScript compression utilities remove all whitespace, rename
variables and obfuscate the code. Resulting files are much smaller.
Progressive Enhancement
• Web-based application sections
1. Structure (HTML)
2. Presentation (CSS)
3. Behavior (JavaScript)
• Goals:
– Keep the three layers completely separate
– Graceful degradation: Users can have JavaScript turned off
and some browsers might not support the latest standards
– Focus on content
– Optimize the user experience regardless of the client platform
Obtrusive JavaScript: that which lives in a web-page
JavaScript is not Java
JavaScript
• Interpreted
• Prototype-based
• Object-based
• Functions and Methods
• First class functions
• Loose dynamic typing
• Function oriented scope
• Special Purpose/embedded
• Dynamic code
Java
• Compiled
• Class based
• Object-oriented
• Methods only
• Methods distinct from data
• Strong static typing
• Block oriented scope
• General purpose/independent
• Static code
Just Like Java (JLJ)
• If and switch statements (Java 1.7 allows switches on strings)
• Basic loop syntax: while and do … while (…);
Java:
JavaScript:
•
•
•
•
•
•
•
•
for (int i=0; i<10; i++)
for (var i=0; i<10; i++)
Reserved words: break, continue, return, this, new
Identifier names: case sensitive, alphanumeric
Comments: // and /* … */
Precedence/operators +=, -=, ++, --, *=, &&, ||, !, etc.
Literals: 123, 1.23, 1.2E+3, 0x123, o777, "123"
String concatenation using the + operator
Escape sequences \t, \n, \b, \125, \xab
Calls to functions and recursion
JavaScript and Java Minor Differences
• JavaScript optional end of line semi-colons (but recommended)
• JavaScript: Dynamic evaluation of strings (ex: eval("var x=20");)
• JavaScript automatically creates a variable that is not explicitly declared
(bad practice because of inadvertent memory leaks).
• JavaScript: console.log Java: System.out.println
• JavaScript: Bitwise operations (&, |, ^, ~, <<,>>,>>>) force conversions
(number to integer and back) are slow and not very useful
• JavaScript: “undefined” for uninitialized variables, missing parameters and
undeclared variables. “null” used by programmers to initialize a null object.
• Double quotes and single quotes can enclose strings.
• Java: final, JavaScript: const (not universally supported)
• JavaScript: Numbers compare equal if same to sixteen digits of precision.
• JavaScript: with(object) to access properties/methods without dot notation
Instanceof vs typeof
• Java: Reference variables are objects, so we use instanceof to test if one object is
an instance of another
• Javascript: The difference between objects and primitives are blurred
Types: Undefined, Null, Boolean, Number, Object, string, and function
o Typeof returns the string representation of an object’s type
“undefined”, "object", "boolean", "number", "object", "string", "function"
o Instanceof returns true if two objects are the same type
• To test if a variable contains a string
o typeof new String("ab") /* object */
new String("ab") instanceof String /* true */
o typeof "abc" /* "string" */
"abc" instanceof String /* false */
o Correct comparison: if (typeof str == 'string' || str instanceof String)
Types
• Java
– Strongly typed: Only the declared type can be stored
– Primitive types:
• int, long, short, double, float, boolean, char, byte, etc.
• Example: int x = 10;
– Reference types: String, and all classes
• JavaScript
– Loosely typed: The last thing stored wins
– Primitive types
• number, boolean, function, string, etc.
• var x = 10;
– Reference types: arrays, all objects, etc.
– Automatic conversions between types are common
• Java: Block level Scope
– A new scope starts with each opening brace
• JavaScript: Functional Scope
Scope
// Works in JavaScript
function doIt()
{ var sum = 0;
for (var i=0;i<10;i++)
{ sum += i; }
for (i=0;i<10;i++)
{ sum += i; }
if (sum > 10)
{ var z = 3; }
alert(sum+" "+i+" "+z);
}
doIt();
– Global Variables: Those declared without var or
outside of all functions
– Local variables: Declared with var in a function
– JavaScript allows functions within functions.
Local variables then live beyond execution
(closure), and are accessible by the subfunctions (lexical scope)
– Scope can be dynamic in that listener methods
within an outer function cannot access its
variables.
– References to declared variables can be deleted:
var x; delete x;
– Variable declarations are hoisted in JavaScript
– Local scope is the function where declared
this: the local environment existing
when a function is called
Built-in Objects
No huge class library, like Java’s
•
•
•
•
•
•
•
•
Number: Contains various formatting methods
Date: Many methods for time and date
Math: Various methods for common calculations
String: Many methods to manipulate strings
Array: A variety of methods for manipulating arrays
RegExp: search or test against regular expressions
DOM based objects: described in the next set of slides
Device: Part of the HTML 5 specification
JavaScript global functions
1.
2.
3.
4.
Not contained in any Object
parseInt(), parseFloat()
isNaN(), isFinite()
eval()
deCodeURI(), enCodeURI()
Purpose: Replace special characters except: ":", "/", ";", and "?" with
%xx, where xx is the corresponding hexadecimal number
var uri="my test.asp?name=ståle&car=saab";
document.write(encodeURI(uri)+ "<br />");
Output: my%20test.asp?name=st%C3%A5le&car=saab
5. escape(), unescape()
Purpose: Spaces, punctuation, accented characters, and any other nonASCII characters are replaced with %xx encoding, where xx is the
corresponding hexadecimal number. Not good for URI encoding.
document.write(escape("Need tips? Visit W3Schools!"));
Output: Need%20tips%3F%20Visit%20W3Schools%21
Best Practice
Bad
var data;
function test()
{
console.log(data);
var data = 4;
console.log(data);
}
/* The first console.log
outputs ‘undefined’ because
the local declaration is
“hoisted” (but without the
initialization) to the top of the
function */
Better
var data;
Function test()
{
var data = 4;
console.log(data);
}
/* Best practice is to declare
all variables at the top of a
function */
Conversions in JavaScript
• Use of Wrapper objects
var num1 = prompt("Enter number: ");
var num2 = prompt("Enter number: ");
var result = Number(num1)+Number(num2);
alert("Result = " + result);
Boolean of 0, -0, "",
var myString = String(result);
false, NaN, undefined,
alert( myString + " " + Boolean(result));
null evaluates to false
• Using parse methods
var number = parseInt(num1,10);
number = parseFloat(num1);
Boolean of anything
else evaluates to true
Note: parseInt and parseFloat convert to the first non digit,
Number() does not; it returns NaN if the conversion fails
Strict Comparisons (=== and !==)
0==false ; // true
0===false; // false, they are different types
1=="1" // true, auto type coercion
1==="1" // false, because different types
1<"3"
// true, auto type coercion
NaN==NaN // false, An object that is not equal to itself
NaN===NaN // false, An object that is not equal to itself
isNaN(NaN); // true
new Number(3)==new Number(3) // false, not the same object
new Number(3)===new Number(3) // false, not the same object
new Number(3).valueOf()==new Number(3).valueOf() // true
Recommendation: Use === and !==; not == and !=
Arrays are not Arrays in the Java Sense
They are also stacks, queues, deques, arraylists, hash tables
Each element can be any type
• Declaration:
var data=[]; or var data=new Array(10); or var data=[1,2,3];
• Manipulate an array
Note: Initializer lists for array indices
are enclosed with square brackets
– Add/Remove to/from end: data.push("a", "b", "c"); or x = data.pop();
– Add/remove to/from front: data.unshift("a", "b”); or x = data.shift();
– Add/Remove/Replace n elements from index x and return removed elements
data.splice(x,0,"a"); or data.splice(x,n); or data.splice(x,2, "a","b");
– Add/Remove associative (hash) elements :
data["age“]=25; (equivalently, data.age=25;)
– Extract part of an array: data.slice(start, end); // start -> end (not including)
– Merging arrays: data.concat(firstData, secondData, … , lastData);
– Merging elements: data.join(";") or data.toString()
Note: Other JavaScript Array methods (ex: sort()) exist
For/In loop
<html><body><script type="text/javascript">
var person={First:"John", Last:"Doe", Age:25};
String key = "Middle“;
var x;
for (x in person)
{
if(person[x]===key)
console.log(person.Last + " " + key);
}
</script></body></html>
Note: First, Last, Age are properties of person,
a polymorphic object (this one lacks Middle)
Warning: For/In loops are very slow, so use with care
Polymorphic Data Structures
• Returning a rectangle data structure
– JavaScript: return { x:10, y:20, width:800, height:400 };
– Java: return new Rectangle( 10, 20, 800, 400 );
• Returning a dimension data structure
– JavaScript: return { width:800, height:400 };
– Java: return new Dimension( 800,400 );
• Polymorphic processing in JavaScript
var rectOrDim = getRectorDimensionFromFunction();
if (rectOrDim.x) { /* Process as a rectangle */ }
else { /* Process as a Dimension */ }
Polymorphic object: one where all of its properties are not required
JavaScript labels
• JavaScript instructions labeled for later reference
• Example
endLoop: for (i=0; i<10; i = i + 1) // Note: JS Lint doesn’t like ++ or –
{ for (j=0; j<10; j = j + 1)
{ for (k=0; k<10; k = k + 1)
if ( i * j * k === someVariable) break endLoop;
}
}
Note: the continue statement goes to the next outer loop
This works in Java, but the language does not guarantee it.
Generally, its use is not best practice.
(Classic CS paper by Dijkstra: “Goto considered harmful”)
User Interface
• Java: AWT or SWING, JavaFX, JOptionPane
• JavaScript: alert, prompt, confirm. Good for debugging,
not recommended for production (blocks the browser).
• JavaScript: Utilize HTML widgets
• JavaScript: Directly write HTML to the web page
– document.write("<h3>Some header</h3>");
– element.innerHTML("<h3>With new line</h3>");
– Framework libraries, like jQuery or Dojo
• Document Manipulation: See next week’s slides
Functions
• Functions can simulate classes, define embedded properties
and methods, and instantiated with the new directive
• Signature lines
o No return type declared
o List parameters without any types
• private and public
o
o
o
o
var x = 10; within a function is private
this.x = 10; within a function is public
this.doSomething = function() { /* … */ } is a public function
var doSomething = function() {/* … */ } is a private function
• Functions can be passed to functions as arguments and
functions can be returned from functions
First class functions: functions that are treated as a data object
Creating an Anonymous Function
Definition: Function without a name
(function ()
{ "use strict"; // Strict syntax verification
var family = ["bill", "joe", "mary"],
var num = family.length,
var data = "";
var i; // Note all variables declared on top
for (i=0; i<num; i++) // Use num, not family.length
data += "<p>" + family[i] + "</p>";
return data;
})(); // This syntax creates and executes the anonymous function
Purpose: Pass as a function argument
Purpose: Encapsulate variables to avoid memory leaks
Anonymous Callback Function
Callback function: passed as an argument and called when an event occurs
window.addEventListener("load",
function (event) // Function with an argument (undefined if not there)
{ "use strict"; // Strict syntax verification (ex: no undeclared variables)
event.preventDefault(); // Prevent browser default behavior
var element = document.getElementsByTagName("article")[0];
var family = ["bill", "joe", "mary"], num = family.length, data = "";
var i;
// Note: all variables are declared on top (no hoisting)
for (i=0; i<num; i++) // Use num, not family.length (much faster)
data += "<p>" + family[i] + "</p>";
Browser default behavior
• Navigate on link click
element.innerHTML = data;
• Right click: context menu
}, false);
• Cause a form submission
• Jumps to other pages
Note: InnerHTML does not actually create DOM elements for the tags
Creating a method in a data object
var persons =
[ { first: "John", last : "Doe", id: 5566,
full: function() {return this.first + " " + this.last);}
,
{ first: "Bill", last: "Miller"}
];
persons[0].full(); or persons[0]["full"]();
Note: persons is an array whose first element contains a method
JSON (Javascript object notation)
Note: Theoretically, you would also
var data = ' { "contacts" :
be able to declare functions in
[ { "name": "john",
JSON. However, JSON is only for
"email": "john@bla.bla", text-based data exchange, so it is
not allowed (major security hole).
},
Note: A string containing text{ "name": "pete",
based JSON can be turned into a
"email": "pete@bla.bla", standard JavaScript object with:
JSON.parse(text);
},
Note: JSON is rapidly becoming
more popular than XML for Web]
services; it is lightweight, smaller,
} ';
and easier to process
data = JSON.parse(data);
Note: JSON.stringify(object)
converts from object to string
alert(data.contacts[0].name);
or alert(data["contacts"][0]["name"]);
John outputs
Nested JSON Example
var data = '{ "id": "0001", "type": "donut", "name": "Cake", "ppu": 0.55,
"batters":
{ "batter":
[ { "id": "1001", "type": "Regular" },
{ "id": "1002", "type": "Chocolate" },
{ "id": "1003", "type": "Blueberry" },
]
},
"topping": [ { "id": "5001", "type": "None" },
{ "id": "5002", "type": "Glazed" },
{ "id": "5005", "type": "Sugar" },
{ "id": "5004", "type": "Maple" }
]
}'
data = JSON.parse(data);
console.log(data.batters.batter[1].type);
Outputs Chocolate
Creating a simple function
function showNames()
{ "use strict"; // Strict syntax verification
var family = ["bill", "joe", "mary"],
var num = family.length,
var data = "";
var i;
// Note all variables declared on top
for (i=0; i<num; i++) // Use num, not family.length
data += "<p>" + family[i] + "</p>";
return data;
}
Anonymous Function Returned
Functions declared as variables and called without their name
function paint(type, color)
{ var str = "The"+type+" is"+ color;
var tellMe = function()
{ document.write(str+".<br> "); }
return tellMe;
}
var say1 = paint("rose", "red");
var say2 = paint("sun", "yellow");
say1(); say2();
Note: The variable, str, still
exists after the call to paint
Completes because it is
needed by subsequent calls
to the anonymous function
Closure: a function that
remembers the environment
in which it was created.
Output:
function () { document.write(str + "."); }
The rose is red. The sun is yellow.
Closure-based object creation
Goal: Define StringBuffer class for fast concatenation (like in Java)
Advantage: Looks more like Java with private/public
instance variables and method
<html<body><script type="text/javascript">
StringBuffer = function() // Siulated Class declaration
{ var buffer = [];
// Private variable
this.append = function(s) {buffer.push(s);}
this.toString = function() { return buffer.join("");};
}
var buffer = new StringBuffer();
Output: Happy Day!
buffer.append("Happy ");
buffer.append("Day!");
document.write("<h3>" + buffer.toString() + "</h3>");
</script></body></html>
Prototypes, not Classes
Java is class-based; JavaScript is a Prototype-based
• There are no classes present. Prototype objects are enhanced
dynamically at run-time
• JavaScript prototypes are of polymorphic associative arrays of
functions and properties
var object = { x:10, doIT: function() { /** some code **/} or
var object = []; object["x"] = 10; object["doIt"] = function() { … };
alert(object.x); or alert(object.doIt());
• Inheritance: achieved by adding constructors to existing objects
• Augmented objects serve as prototypes for even newer objects.
Prototype-based properties
Output: 1 Value = 1 undefined
var MyClass = function(){};
var my = new MyClass(); // Create an instance
MyClass.x = 2; // Add a property to future objects
MyClass.prototype.y = 1; // Add a prototype property
// Add a toString() prototype method
MyClass.prototype.toString = function()
{ return "Value="+this.x + " " + this.y; }
document.writeln(my.toString());
Note: Every object in JavaScript has a prototype object; adding
to it affects all instances, even those previously instantiated
JavaScript Prototype Chain
Allows object inheritance
Caution: Inheritance leads to significant runtime slowdowns
• All JavaScript objects have a prototype object. JavaScript will
attempt to find a property in an object first. If it is not there,
JavaScript then refers to the object’s prototype, and so on
through sub-objects in the prototype object.
• Prototype inheritance chains can nest without limit. In general,
chains should be short. Long chains lead to bad performance
JavaScript Inheritance
Cat Twigs says Meow
Dog Spot says Woof
function Pet(name) // Base class
{
this.name = name; this.who = function() { return name; }
}
Pet.prototype = { species: 'animal', sleep: function() { return name + ' zzzz'; } }
function Cat(name) { Pet.apply(this, arguments); } // Derived class constructor
Cat.prototype = Object.create(Pet.prototype); // Set prototype base class (Pet)
Cat.prototype.constructor = Cat; // Designate constructor; not Pet
Cat.prototype.speak = function() { return "Meow"; } // Extension function
function Dog(name) { Pet.apply(this, arguments); } // Derived class constructor
Dog.prototype = Object.create(Pet.prototype);
Note: The latest ECMA
Dog.prototype.constructor = Dog;
standard has class and
Dog.prototype.speak = function() { return "Woof"; }
extends reserved
words
// Example using inheritance
var cat = new Cat("Twigs"), dog = new Dog("Spot");
document.write("<h3>Cat "+cat.sleep()+"</h3>");
document.write("<h3>Dog " + dog.who() + " says " + dog.speak()) + "</h3>";
Add String Class trim() Methods
<html><head><title>Add Trim()</title></head>
<body><script type="text/javascript">
String.prototype.ltrim = function()
{ return this.replace(/^\s+/,''); }
String.prototype.rtrim = function()
{ return this.replace(/\s+$/,''); }
String.prototype.trim = function()
{ return this.ltrim().rtrim(); }
var text = "
abc ", text2 = "
def
".trim();
document.write("<h3>"+text.trim()+text2+"</h3>");
</script></body></html>
JavaScript is more lightweight than Java
Programmers add functionality; Applications do not have unneeded features
Note: Any JavaScript core object, (like Array, Date, Math, Number, RegExp)
can be extended with new properties and methods
Regular Expression Review
In JavaScript: / pattern /
Used by String match and replace methods
1.
2.
3.
4.
5.
6.
7.
8.
9.
^ - beginning of string, $ - end of string
\s , \d, \w – white space, digit, word (identifier) respectively
\S, \D, \W – Not white space, digit, word respectively
[abcde] or [a-e] – any of the characters a through e
. (period) matches any character
abc|def match abc or def
\n, \r, \t – new line, carraige return, tab respectively
* - zero or more times, + - one or more time, ? – zero or one time
{3,6} – three through six times, {,4} – up to four times
Regular Expression Examples
var data = "(322)- abc-4568"
1. Remove all spaces and dashes
var reg=/[ -]+/g; out = data.replace(reg, "");
2. Remove all parentheses, spaces, dashes
var reg = new RegExp("/[() -]+/g");
out = data.replace(reg, "");
3. Remove non-digits: out = data.remove(/\D/g, "");
4. Remove non-alphabetic characters from front
data.remove(/^\W/, ""); or data.remove( /^[0-9a-zA-Z]+/, "");
5. Phone number: first remove white space, parens, & dashes
var reg = /^\d{3}([A-Za-z0-9]{7}$/;
if (data.match(reg) === true) console.log("okay");
Overloading Functions
Not the way that Java does it
function Test(a, b)
{ if (b) {/* or if (b!==undefined), do something */}
else
{ var t = typeof(a); // Get type of first argument
switch (t) // Execute code based on the type
{ case "number" : /* Number Stuff */ break;
case "string" : /* String Stuff */ break;
case "boolean" : /* Boolean Stuff */ break;
case "object" : /* Object Stuff */ break;
default: alert("No overload for type: " + t);
}
}
}
Variable Number of Arguments
function AddNumbers()
{
var sum = 0;
for(var i=0; i<arguments.length; i++)
{
sum += arguments[i];
}
return sum;
}
Use the arguments object
Browser Detection
Browser specific code sometimes needed for cross-platform compatibility
• Check user agent:
"android", "ipod", "iphone", "chrome", "opera", "firefox",
"safari"
function isAndroid()
{
var browser = navigator.userAgent;
return browser.toLowerCase().indexOf("android")>=0;
}
• Check appName for Explorer:
(different than all other browsers)
this.isExplorer = function()
{
var browser = navigator.appName;
return browser.toLowerCase().indexOf("microsoft")>=0;
}
Functionality Detection
Browsers don’t support all of the audio codecs
var audio = [];
audio["mp3"] = false; audio["ogg"] = false; audio["wav"] = false;
try
{ var test = document.createElement('audio');
audio["bgsound"] = this.isExplorer();
// Currently canPlayType(type) returns: "", "maybe" or "probably"
audio["mp3"] = test.canPlayType && test.canPlayType('audio/mpeg') != "";
audio["ogg"] = test.canPlayType
&& test.canPlayType(' audio/ogg; codecs="vorbis" ') != "";
audio["wav"] = test.canPlayType && test.canPlayType('audio/wav‘!="");
} catch(e) { }
if (!audio["ogg"]) // Doesn’t handle OGG
Try Catch to Detect Functionality
try
{ lesson = new Lesson(type, lessons[num]);
}
catch (e)
{ showMessage(e.name+" "+e.message+" "+
e.toString()+" " + e.stacktrace);
}
Note: Throw clause in JavaScript
• Note: throw( /*some string, integer, boolean, or object */)
• Example: throw ("Lesson "+type+" not supported\n"+e.stack);
JavaScript Security Policy
• Sandbox:
– JavaScript can only perform web-related actions, they
cannot create files or access the client’s file system. There
are, however, tentative plans to relax this somewhat.
– There is the ability to use a local storage facility provided
by HTML5 for persisting name/value pairs. Older cookie
based save/load is also possible.
• Same Origin Policy:
– scripts from one web site cannot access usernames,
passwords, or cookies sent to another site or created by a
script from another web site
Security Vulnerabilities
• Poorly designed JavaScript code: Secret information
embedded in JavaScript cannot be hidden from attackers.
• Browser and plug-in coding errors: Exposes flaws, such as
buffer overflow or enabling access to client side facilities
• JavaScript hijacking: Exploits a page that returns private
unencrypted information in JSON or XML formats.
• Cross-site-scripting (XSS): malicious script that alters web
pages. The victim may then disclose privileged information
• Cross-site request forgery vulnerability (CSRF): Attacker code
tricks the victim's browser into taking unintended actions
Download