Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions src/main/html/Integration.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<!doctype html>
<html lang="en">
<head>
<title>Integration</title>
<script src="https://cdn.tiny.cloud/1/b6r7yu1n19ibkha4z0s4gdvt0l7z2mc3dzlnc780ep0uuzks/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body>
<textarea class='classic'></textarea>
<script>
tinymce.init({
selector: '.classic',
content_style: 'body {font-size: 14pt;}',
plugins: 'table spellchecker fullscreen code print link image lists advlist wordcount autoresize',
menubar: 'file edit view insert format tools table',
formats: {
myred: { inline: 'span', styles: { color: 'red' } }
},
menu: {
file: { title: 'File', items: 'newdocument restoredraft | preview | print ' },
edit: { title: 'Edit', items: 'undo redo | cut copy paste | selectall | searchreplace' },
view: { title: 'View', items: 'spellchecker | preview fullscreen' },
insert: { title: 'Insert', items: 'inserttable' },
format: { title: 'Format', items: 'bold italic underline strikethrough' },
tools: { title: 'Tools', items: 'spellchecker' },
table: { title: 'Table', items: 'inserttable | cell row column | tableprops deletetable' },
},
toolbar: 'undo redo | bold italic | alignleft aligncenter alignright alignjustify | fontselect fontsizeselect formatselect | bullist numlist outdent indent | link image | fullscreen wordcount code print | customToolBarButton',
max_height: 230,
setup: function (editor) {
editor.ui.registry.addButton('customToolBarButton', {
text: 'My Custom Button',
onAction: function () {
// alert('Button clicked!');
tinymce.activeEditor.windowManager.open({
title: 'Custom dialog',
body: {
type: 'panel',
items: [
{
type: 'input',
name: 'customInput',
label: 'Please enter your fruit'
}
]
},
buttons: [
{
type: 'submit',
text: 'Submit'
}
],
onSubmit: function (dialogApi) {
editor.formatter.apply('myred');
editor.execCommand('mceInsertContent', false, dialogApi.getData().customInput);
editor.formatter.remove('myred');
dialogApi.close();
}
});
}
});
}
});
</script>

<h1>Inline editor</h1>
<form method='post'>
<div class='inlineEditor'></div>
</form>
<script>
tinymce.init({
selector: '.inlineEditor',
inline: true,
formats: {
myred: { inline: 'span', styles: {color: 'red'}}
},
placeholder: 'click here to edit .....',
setup: function (editor) {
// context button
editor.ui.registry.addButton('customStrikeButton', {
text: 'strike red',
icon: 'cancel',
tooltip: 'strike through and change colour to red',
onAction: function (_) {
editor.formatter.apply('strikethrough');
editor.formatter.apply('myred');
}
});
// context tool bar
editor.ui.registry.addContextToolbar('imagealignment', {
predicate: function (node) {
return !editor.selection.isCollapsed();
},
items: 'bold italic underline customStrikeButton',
position: 'selection',
scope: 'editor'
});
},
});
</script>

<h1>All editors</h1>
<button onclick='gatherAll()'>Collect all</button>
<div id='container' style='border: 1px solid; min-height: 400px;'>

</div>
<script>
function gatherAll() {
document.getElementById('container').innerHTML = tinymce.get(0).getContent() + '<br/>' + tinymce.get(1).getContent();;
}
</script>
</body>
</html>
14 changes: 9 additions & 5 deletions src/main/html/Part1Ex1.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

TODO Head over to https://www.tiny.cloud/ and sign up for an API key. Replace the 'no-api-key' with your api key in the below script tag.
-->
<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
<script src="https://cdn.tiny.cloud/1/b6r7yu1n19ibkha4z0s4gdvt0l7z2mc3dzlnc780ep0uuzks/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
</head>
<body>

Expand All @@ -30,9 +30,11 @@
Since a selector can match multiple elements, you can use tinymce.init to create multiple editors.
TODO In the below script tag, instantiate TinyMCE editors in the below
-->
<textarea class="editorSet"></textarea>
<textarea class="editorSet"></textarea>
<script></script>
<textarea id="editor2" class="editorSet"></textarea>
<textarea id="editor3" class="editorSet"></textarea>
<script>
tinymce.init({ selector: '.editorSet' });
</script>


<!--
Expand All @@ -54,7 +56,9 @@

Note: you'll need to add some plugins for these to work.
-->

<h1>Custom Editor</h1>
<textarea id="customEditor"></textarea>
<script>tinymce.init({selector: "#customEditor", menubar: false, status: false, plugins: "fullscreen table code", toolbar: "bold table fullscreen code"})</script>

</body>

Expand Down
11 changes: 7 additions & 4 deletions src/main/ts/Part2Ex1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Let's model the x,y of the top-left and bottom-right corners.
*/
export interface Boundz {
// TODO: add fields: x1, y1, x2, y2
readonly x1: number;
readonly y1: number;
readonly x2: number;
readonly y2: number;
}

/*
Expand All @@ -27,10 +31,7 @@ We tell tsc to transpile to ES5, so IE works.
Notice also that we have an explicit return type. This lets the compiler check that our
code matches the type signature.
*/
export const width = (b: Boundz): number =>
/* TODO */ -1;

// TODO implement height function
export const width = (b: Boundz): number => b.x2 - b.x1;

/*
3. Compiling.
Expand All @@ -47,3 +48,5 @@ Ok, so we started off pretty easy.
Now, code is useless without tests, so let's head over to Exercise1CodeStyleTest.ts
and write some tests.
*/

export const height = (b: Boundz): number => b.y2 - b.y1;
18 changes: 8 additions & 10 deletions src/main/ts/Part2Ex2ArrayFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ We don't write loops if we can help it. Instead, we go up a level, and call func
The simplest of these is 'each' which just iterates.

TODO: Run the following code using this command:
yarn bedrock-auto -b chrome-headless -f src/test/ts/Exercise2ArrayFunctionsTest.ts
yarn bedrock-auto -b chrome-headless -f src/test/ts/part2/Exercise2ArrayFunctionsTest.ts
*/

export const runEach1 = (): void => {
Expand Down Expand Up @@ -41,6 +41,7 @@ export const myFrogs: Frog[] = [

export const runEach2 = (): void => {
// TODO: Use Arr.each and console.log to print the name of each frog
Arr.each(myFrogs, (f) => console.log(f.name))
};

/*
Expand All @@ -65,8 +66,7 @@ export const runMap2 = (xs: number[]): string[] =>

// TODO: Return the frog's names and check it by running
// yarn bedrock-auto -b chrome-headless -f src/test/ts/Exercise2ArrayFunctionsTest.ts
export const frogNames = (fs: Frog[]): string[] =>
[];
export const frogNames = (fs: Frog[]): string[] => Arr.map(fs, (f) => f.name);

// TODO: Return the frog's ages
// TODO: Write a test for this in Exercise2ArrayFunctionsTest
Expand All @@ -83,12 +83,10 @@ export const evens = (xs: number[]): number[] =>

// TODO: Write a function that returns all the frogs that ribbit
// TODO: Run the provided test to check your answer.
export const ribbitting = (frogs: Frog[]): Frog[] =>
[];
export const ribbitting = (frogs: Frog[]): Frog[] => Arr.filter(frogs, (f) => f.ribbits);

// TODO: Write a function that returns all frogs aged 8 or older
export const olderFrogs = (frogs: Frog[]): Frog[] =>
[];
export const olderFrogs = (frogs: Frog[]): Frog[] => Arr.filter(frogs, (f) => f.age >= 8);

/*
5. Arr.exists
Expand All @@ -97,8 +95,9 @@ Arr.exists returns true if there is one or more element that matches a predicate
*/

// TODO: Write a function that returns true if there's one or more ribbiting frogs

export const oneOrMoreRibbitingFrogs = (frogs: Frog[]) => Arr.exists(frogs, (f: Frog) => f.ribbits === true);
// TODO: Write a function that takes an array of numbers, and returns true if there are any negative numbers
export const hasNegativeNumber = (items: number[]) => Arr.exists(items, (i: number) => i < 0);

/*
6. Arr.bind
Expand All @@ -110,8 +109,7 @@ This behaviour of running map then flatten is why this function is sometimes cal

TODO: Write a function that takes a list of strings, each string containing a comma-separated list of values, and returns all of the values as an array.
*/
export const splitCsvs = (csvs: string[]): string[] =>
[];
export const splitCsvs = (csvs: string[]): string[] => Arr.bind(csvs, (s) => s.split(','));

/*
7. Arr.find
Expand Down
28 changes: 20 additions & 8 deletions src/main/ts/Part2Ex3Optional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ export const toPositiveInteger = (n: number): Optional<number> =>
n > 0 ? Optional.some(n) : Optional.none();

// TODO: create a function which takes a string and returns some if the string is non-empty
export const isString = (s: string): Optional<string> => s.length > 0 ? Optional.some(s) : Optional.none();

// TODO: create a function which takes a url as a string and returns the protocol part as an Optional.
// The string may or may not actually have a protocol. For the protocol to be valid, it needs to be all alpha characters.
// You can use a regex.
// Have a look at Exercise3OptionTest.ts for example input. Make sure the tests pass.
export const getProtocol = (url: string): Optional<string> => {
throw new Error("TODO");
let regexMatch = url.match(/^http(s)?:\/\//);
return regexMatch !== null ? Optional.some(regexMatch[0].replace('://', '')) : Optional.none();
};

/*
Expand All @@ -57,10 +59,13 @@ TODO: use Optional.from to implement the following DOM function
*/

export const getNextSibling = (e: Element): Optional<ChildNode> => {
throw new Error("TODO");
return Optional.from(e.nextSibling);
};

// TODO: use Optional.from to implement a similar wrapper for Element.getAttributeNode(string)
export const getElementAttribute = (e: Element, attributeName: string): Optional<Attr> => {
return Optional.from(e.getAttributeNode(attributeName));
}

/*
How do we get data out of an Optional? Well, that's a bit tricky since there isn't always
Expand All @@ -80,11 +85,10 @@ export const message = (e: Optional<string>): string =>
);

// TODO: Implement a function using fold, that takes an Optional<number>. If it's some, double it. If it's none, return 0;
export const doubleNumber = (n: Optional<number>): number => n.fold(() => 0, (n) => n * 2);

// TODO: Implement a function that takes an Optional<T> for any type T. Return true if it's some, and false if it's none.
const trueIfSome = <T> (x: Optional<T>): boolean => {
throw new Error("TODO");
};
const trueIfSome = <T> (x: Optional<T>): boolean => x.fold(() => false, () => true);

/*
The last function you implemented is already part of the Optional type, and is called isSome().
Expand All @@ -107,8 +111,10 @@ You can do this with fold, but getOr is a shortcut.
*/

// TODO: Using getOr, take an Optional<{age: number}> and turn it into an {age: number}, using a default value of 0.
export const toValueOr = (input: Optional<{age: number}>): {age: number} => input.getOr({age: 0});

// TODO: Write the same function using fold
export const toValueOrWithFold = (input: Optional<{age: number}>): {age: number} => input.fold(() => ({age: 0}), (input: {age: number}) => input);


/*
Expand All @@ -118,9 +124,13 @@ Let's explore this by converting Optionals to and from Arrays.
*/

// TODO: Write a function that converts an Optional<A> to an A[] for any type A.

export const optionalToArray = <A>(input: Optional<A>): A[] => {
return input.fold(() => [], (value) => [value]);
}
// TODO: Write a function that converts an A[] to an Optional<A>. If the array has more than one element, only consider the first element.

export const arrayToOptional = <A>(input: A[]): Optional<A> => {
return Optional.from(input[0]);
}

/*
One of the most useful functions on Optional is "map". We say this function "maps a function over the Optional".
Expand All @@ -139,12 +149,14 @@ const x: Optional<string> = Optional.some(3).map((x) => String(x)); // returns O
const y: Optional<string> = Optional.none<number>().map((x) => String(x)); // returns Optional.none<string>()

// TODO: Write a function that takes an Optional<number> and adds 3 to the number
export const add3 = (number: Optional<number>) => number.map((x) => x + 3);

// TODO: Write a function that takes an Optional<string> and prefixes the string with "hello"
export const prefixHello = (input: Optional<string>) => input.map((i) => "hello " + i);

/*
TODO: If the below function is called, does it return a value or throw an exception? Why should it behave one way or the other?
Answer: ...
Answer: return Optional.none() since there is nothing to runs the function over
*/
const willItKersplode = (): Optional<string> => {
const z = Optional.none<string>();
Expand Down
24 changes: 17 additions & 7 deletions src/main/ts/Part2Ex4FP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,15 @@ TODO: Extract a pure function for the logic hiding in this (impure) function

type Mode = 'code' | 'design' | 'markdown';

const hasMode = (m: Mode): boolean => {
return m === 'code' || m === 'design' || m === 'markdown' ? true : false;
}

const switchMode = (m: Mode): void => {
const valid = hasMode(m);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't really do anything meaningful unless you're accepting content from outside of TypeScript, as you can't pass a mode that isn't defined in the Mode type and if you're checking this it's somewhat defeating the point of using types. That said, you've still made a pure function here so its fine as that was the point of this exercise.

if (valid) {
// do side effect
}
// pretend that something useful happens here that causes a side effect
};

Expand Down Expand Up @@ -120,12 +128,12 @@ const getOrElse1 = <A> (oa: Optional<A>, other: A): A =>
// Hang on - that looks familiar. The function we pass as the "some" case is the identity function.

// TODO: write a version of getOrElse1 using Fun.identity.

export const getOrElse2 = <A> (oa: Optional<A>, other: A): A => oa.fold(() => other, Fun.identity);
// TODO: What happens if you map the identity function over an Optional?
// Answer: ...
// Answer: Get the value of the Optional

// TODO: What happens if you map the identity function over an Array?
// Answer: ...
// Answer: Get the value of each element in the array

/*
In FP, we use a lot of little functions like identity, that seem insignificant on their own, but they come in handy
Expand All @@ -149,17 +157,19 @@ Again, this looks familiar from our getOrElse1 function above.

TODO: rewrite getOrElse1 using both Fun.identity and the "constant" function defined above.
*/

export const getOrElse3 = <A> (oa: Optional<A>, other: A): A => oa.fold(constant(other), Fun.identity)

/*
TODO: use katamari's Fun.constant in your getOrElse and see if it compiles.
*/
export const getOrElse4 = <A> (oa: Optional<A>, other: A): A => oa.fold(Fun.constant(other), Fun.identity)

// TODO: Write a function that takes an array of numbers and replaces each value with 9.
export const replaceElementWith9 = (items: number[]) => Arr.map(items, Fun.constant(9));


// TODO: In the previous question, what's the *same* between the input and output values
// Answer:
// Answer: They are the same type (number) and same number of elements??


/*
Expand Down Expand Up @@ -210,7 +220,7 @@ signature and handling for n-ary functions. Your rule-of-thumb is to use Fun.com
*/

// TODO: use Fun.compose1 to write a function that doubles a number twice
export const dblX2: (x: number) => number = Fun.compose1(dbl, dbl);

// TODO: Rewrite this function to use a single map call and function composition
const dblOs = (oa: Optional<number>): Optional<string> =>
oa.map(dbl).map(String);
export const dblOs = (oa: Optional<number>): Optional<string> => oa.map(Fun.compose1(String, dbl));
Loading