PageSourceSearch

https://kickstartcoding.com/_modulo/modulo-build-xxi33g23.js

js kickstartcoding.com collected 2026-10-04 12:38:12 UTC 310,944 bytes, 3,095 lines download raw bytes

1
2window.moduloBuild = window.moduloBuild || { modules: {}, nameToHash: {} };
3
4window.moduloBuild.modules["x1h0vjl4"] = function configuration (modulo) {
5
6     function setupFathom(code) {
7        // Check common things we skip
8        const host = window.location.host;
9        if (host.startsWith('localhost') ||
10                host.includes('.local') ||
11                host.startsWith('127') ||
12                host.startsWith('192') ||
13                host.startsWith('dev--'))  {
14            return;
15        }
16        (function(f, a, t, h, o, m){
17            a[h]=a[h]||function(){
18                (a[h].q=a[h].q||[]).push(arguments)
19            };
20            o=f.createElement('script'),
21            m=f.getElementsByTagName('script')[0];
22            o.async=1; o.src=t; o.id='fathom-script';
23            m.parentNode.insertBefore(o,m)
24        })(document, window, '//an.michaelb.org/tracker.js', 'fathom');
25        fathom('set', 'siteId', code);
26        fathom('trackPageview');
27    }
28    setupFathom('JUSMY');
29
30};
31window.moduloBuild.nameToHash.configuration = "x1h0vjl4";
32
33window.moduloBuild.modules["xxkop4gm"] = function configuration1 (modulo) {
34// This is very useful, and may make it into core Modulo
35// Uses:
36// - Adding head content that gets idempotently added in a natural way:
37// - <template Include><script src="whatevs"></script></template>
38// - Using it like a mini NPM:
39// - <Include showdown="1.3.4" neat-widgets="4.2.6"></Include>
40// - <Include create-modulo></Include> To bring in CMTK!
41// Eventually, a cool idea would be define named ones at top, and include
42// lazily if scripts are loaded (e.g. <Include jquery></Include> in a
43// component) -- might work?
44//Server: 'unpkg.com',
45modulo.config.include = {
46    GlobalBundled: { },
47    LoadMode: 'bundle',
48    ServerTemplate: 'https://{{ server }}/{{ path }}',
49    TagTemplate: '{% if isCSS %}<link name="{{ package }}" rel="stylesheet" href="{{ url }}" />' +
50                  '{% else %}<script name="{{ package }}" src="{{ url }}"></' + 'script>{% endif %}',
51    DefLoaders: [ 'DefTarget', 'DefinedAs', 'Src', 'Server', 'LoadMode' ],
52};
53
54modulo.register('util', function loadHead(modulo, loadMode, elem, knownBundled) { // TODO refactor with AssetManager?
55    const { newNode, hash } = modulo.registry.utils;
56    const id = hash(elem.name || elem.src || elem.href || elem.tagName);
57    if (window.document.getElementById(id) || knownBundled[id]) {
58        return; // already exists, never add twice
59    }
60    knownBundled[id] = loadMode === 'bundle'; // (set to true if bundling this)
61    const newElem = newNode(elem.innerHTML, elem.tagName);
62    for (const attr of elem.attributes || []) { // Copy all attributes from old elem
63        newElem.setAttributeNode(attr.cloneNode(true)); // ...to new elem
64    }
65    newElem.setAttribute('id', id); // TODO: Create a "setAttrs" func for this, or enhance newNode
66    if (loadMode !== 'bundle') {
67        newElem.setAttribute('modulo-asset', 'y');
68    }
69    /*if (elem.tagName === 'SCRIPT') { // Need to fix SCRIPT tag fluke
70        newElem.src = elem.src; // (TODO: Still needed?)
71    }*/
72    window.document.head.append(newElem);
73});
74
75modulo.register('cpart', class Include {
76    static LoadMode(modulo, def, value) { // Loads given Include def
77        const { loadHead, newNode, keyFilter } = modulo.registry.utils;
78        const lower = key => key[0].toLowerCase() === key[0]; // skip "-prefixed"
79        const ctx = { def, modulo, headContent: def.Content || ''  };
80        const render = code => new modulo.registry.cparts.Template(code).render(ctx);
81        for (const [ pkg, v ] of Object.entries(keyFilter(def, lower))) {
82            ctx.path = v.contains('@') ? v : pkg + (v ? ('@' + v) : '');
83            ctx.isCSS = (ctx.path.contains('/') && ctx.path.endsWith('.css'));
84            ctx.url = render(def.ServerTemplate); // Render URL using ServerTemplate
85            ctx.headContent += render(def.TagTemplate); // Append tag to head Content
86        }
87        for (const elem of newNode(ctx.headContent).children) { // Loop through combined
88            loadHead(modulo, value, elem, modulo.config.include.GlobalBundled);
89        }
90    }
91    intitializedCallback(renderObj) {
92        const { Include } = this.modulo.registry.cparts; // Can I use self. reliably?
93        Include.LoadMode(this.modulo, this.attrs, 'lazy', { }); // Always load if ID not exist
94    }
95});
96modulo.register('coreDef', modulo.registry.cparts.Include);
97
98};
99window.moduloBuild.nameToHash.configuration1 = "xxkop4gm";
100
101window.moduloBuild.modules["x1ohdeuk"] = function library_configuration (modulo) {
102
103    modulo.register('util', function loadLink(href, loadMode) { // TODO refactor with AssetManager?
104        const { newNode, hash } = modulo.registry.utils;
105        const id = hash(href);
106        if (window.document.getElementById(id)) {
107            return; // already exists, never add twice
108        }
109        const newElem = newNode('', 'link');
110        newElem.setAttribute('id', id);
111        newElem.setAttribute('rel', 'stylesheet');
112        let proto = window.location.protocol;
113        newElem.setAttribute('href', proto + href);
114        if (loadMode !== 'bundle') {
115            newElem.setAttribute('modulo-asset', 'y');
116        }
117        window.document.head.append(newElem);
118    });
119    modulo.registry.utils.loadLink('//fonts.googleapis.com/css?family=Raleway:300,400,700,800%7CLato:300,400,700');
120    modulo.registry.utils.loadLink('//cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css');
121
122};
123window.moduloBuild.nameToHash.library_configuration = "x1ohdeuk";
124
125window.moduloBuild.modules["xx5qsu45"] = function library1_configuration (modulo) {
126
127    modulo.register('templateFilter', function hlinperson (text) {
128        return text
129            .replace(/(in.person)/gi, '<span class="text--hlinperson">$1</span>')
130            .replace(/(on.site)/gi, '<span class="text--hlinperson">$1</span>');
131    });
132
133};
134window.moduloBuild.nameToHash.library1_configuration = "xx5qsu45";
135
136window.moduloBuild.modules["xxp4is5b"] = function library_Page (modulo) {
137const def = modulo.definitions['library_Page'];
138class x_Page extends window.HTMLElement {
139constructor() { super(); this.init(); }
140}
141modulo.registry.utils.initComponentClass(modulo, def, x_Page);
142window.customElements.define(def.TagName, x_Page);
143return x_Page;
144
145};
146window.moduloBuild.nameToHash.library_Page = "xxp4is5b";
147
148window.moduloBuild.modules["xxln9ofu"] = function library_FeaturedTestimonials (modulo) {
149const def = modulo.definitions['library_FeaturedTestimonials'];
150class x_FeaturedTestimonials extends window.HTMLElement {
151constructor() { super(); this.init(); }
152}
153modulo.registry.utils.initComponentClass(modulo, def, x_FeaturedTestimonials);
154window.customElements.define(def.TagName, x_FeaturedTestimonials);
155return x_FeaturedTestimonials;
156
157};
158window.moduloBuild.nameToHash.library_FeaturedTestimonials = "xxln9ofu";
159
160window.moduloBuild.modules["x1b97eng"] = function library_Testimonials (modulo) {
161const def = modulo.definitions['library_Testimonials'];
162class x_Testimonials extends window.HTMLElement {
163constructor() { super(); this.init(); }
164}
165modulo.registry.utils.initComponentClass(modulo, def, x_Testimonials);
166window.customElements.define(def.TagName, x_Testimonials);
167return x_Testimonials;
168
169};
170window.moduloBuild.nameToHash.library_Testimonials = "x1b97eng";
171
172window.moduloBuild.modules["xx7g6r2p"] = function library_FeaturedArticles (modulo) {
173const def = modulo.definitions['library_FeaturedArticles'];
174class x_FeaturedArticles extends window.HTMLElement {
175constructor() { super(); this.init(); }
176}
177modulo.registry.utils.initComponentClass(modulo, def, x_FeaturedArticles);
178window.customElements.define(def.TagName, x_FeaturedArticles);
179return x_FeaturedArticles;
180
181};
182window.moduloBuild.nameToHash.library_FeaturedArticles = "xx7g6r2p";
183
184window.moduloBuild.modules["x11i3991"] = function library_Page_script (modulo) {
185var script = { exports: { } }; 
186        function prepareCallback() {
187            // For now, store the navigation array and generate year in JS
188            return {
189                currentYear: (new Date()).getFullYear(),
190                navigation: [
191                    {
192                        title: "Start",
193                        file: "index.html",
194                    },
195                    {
196                        title: "Courses",
197                        file: "courses/",
198                    },
199                    {
200                        title: "Plans",
201                        //file: "index.html#learn",
202                        file: "apply/",
203                    },
204                    {
205                        title: "Services",
206                        file: "services/",
207                    },
208                    /*
209                    {
210                        title: "Results",
211                        file: "results/",
212                    },
213                    {
214                        title: "Learn",
215                        //file: "index.html#learn",
216                        file: "learn/",
217                    },
218                    {
219                        title: "Outreach",
220                        file: "outreach/",
221                    },
222                    */
223                ],
224            };
225        }
226     ;return { exports: script.exports,  "prepareCallback": typeof prepareCallback !== "undefined" ? prepareCallback : undefined,  setLocalVariables: function (o) {  } }
227};
228window.moduloBuild.nameToHash.library_Page_script = "x11i3991";
229
230window.moduloBuild.modules["xxbcstkp"] = function library_FeaturedTestimonials_staticdata (modulo) {
231return [{"name":"Anthony Sifontes","img":"anthony.jpg","location":"New Orleans, Louisiana","profession":"Software Engineer @ Amino","linkedin":"anthonysifontes","featured":"Software Engineer","pullquote":"I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.","text":"\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        "},{"name":"Andrew Thorton","img":"andrew.jpg","location":"Alameda, California","profession":"Data Engineer @ Lyft","featured":"Data Engineer","linkedin":"andrew-w-thornton","pullquote":"Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.","text":"\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focu
231sed on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        "},{"name":"Nicol Hawkins","featured":"Platform Specialist","img":"nicol.jpg","location":"San Franciso, California","profession":"Platform Specialist @ Amplitude","linkedin":"nicol-hawkins","featured_on_frontpage":true,"pullquote":"Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.","featured_text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        ","text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        "}];
232};
233window.moduloBuild.nameToHash.library_FeaturedTestimonials_staticdata = "xxbcstkp";
234
235window.moduloBuild.modules["xxnr3p18"] = function library_Testimonials_staticdata (modulo) {
236return [{"name":"Nicolas Miller","img":"nicolas.jpg","location":"Antioch, California","profession":"Software Engineer @ Rooster Grin Media","linkedin":"nicolas-wyatt-miller","pullquote":"The custom paced structure was possibly the most vital component to\n            my success [...] this setup allowed me to pace my progress and\n            uphold my life's balance in the process.","text":"\n            <strong>It sounds cheesy, but Kickstart Coding gave me the\n            opportunity to make a career shift that I never thought I would be\n            able to.</strong> The personalized tutoring helped target my\n            weaknesses and get honest feedback on my projects that large scale\n            programs are unable to provide. The custom paced structure was\n            possibly the most vital component to my success; I've never been\n            the person who gets something new on the first, second, or even\n            third try, but this setup allowed me to pace my progress and uphold\n            my life's balance in the process. The instructional material was\n            intense, but Michael makes some hilarious references and segments\n            the topics into digestible lessons. If I could give any advice, I\n            would say to not let anything stop you, but ESPECIALLY don't let\n            you stop yourself. You 1000% can do this and self doubt or fear of\n            difficulty are likely to be your biggest obstacles -- you just have\n            to be kind to yourself and get up again when JavaScript and CSS\n            inevitably knock you down. <strong>You are going to be so proud of\n            yourself because this is massive and this is worth it!</strong>\n        ","featured":null},{"name":"Anthony Sifontes","img":"anthony.jpg","location":"New Orleans, Louisiana","profession":"Software Engineer @ Amino","linkedin":"anthonysifontes","featured":null,"pullquote":"I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.","text":"\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        "},{"name":"Andrew Thorton","img":"andrew.jpg","location":"Alameda, California","profession":"Data Engineer @ Lyft","featured":null,"linkedin":"andrew-w-thornton","pullquote":"Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.","text":"\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focu
236sed on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        "},{"name":"Sarah Farnsworth-Kumli","img":"sarah.jpg","location":"San Francisco, California","profession":"Solutions Engineer @ Okta","linkedin":"sarah-fk","pullquote":"I walked into Kickstart Coding to brush up on my skills. I\n        left with a new career.","text":"\n            <strong>Now that I am on the other side, have landed a job, and am\n            now working with developers I can honestly say that Kickstart is a\n            quality program.</strong> They take a modern approach to teaching\n            and teach what's current in an adapting industry (also what stacks\n            companies are hiring for!).\n\n            Michael covered CS concepts in an incredibly accessible way for a\n            diverse classroom with different levels of experience.  The course\n            material was the perfect balance of providing introductory guidance\n            and challenge. Group projects exposed us to the challenges of\n            working in a development team and we continued to build on new\n            iterations of our personal projects.\n\n            It's a privilege to have been a part of such a fantastic community,\n            and I'm excited to see it grow. <strong>Amazing how such a short\n            time can change your life, if you're considering taking the plunge,\n            DO IT!</strong>\n        ","featured":null},{"name":"Nicol Hawkins","featured":null,"img":"nicol.jpg","location":"San Franciso, California","profession":"Platform Specialist @ Amplitude","linkedin":"nicol-hawkins","featured_on_frontpage":true,"pullquote":"Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.","featured_text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        ","text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        "},{"name":"Yulia Tuzov","img":"yulia.jpg","location":"San Mateo, California","profession":"QA engineer @ Skupos","linkedin":"yuliatuzov","pullquote":"Kickstart’s curriculum gave me technical understanding of\n        all the moving parts involved in building a software product.","text":"\n            I have been in tech for couple of years in a QA role prior to going\n            to this bootcamp.  And now transitioning into an engineering role.\n            Kickstart’s curriculum gave me technical understanding of all the\n            moving parts involved in building a software product as well as\n            practice of building those parts in group projects and individual\n            projects which is extremely valuable experience.\n            \n            The curriculum overall is very well structured in a way that after\n            every topic is introduced students had to apply it in their\
236n            homework right away as well as final projects at the end of each\n            course. Program also includes industry insights and job prep\n            material. <strong>Michael, Mark and Maddy are very passionate about\n            their students to succeed in the career transitions. You do have to\n            work a lot during the course, so be ready, but you’ll also have\n            amazing support.</strong> Support also doesn’t end after the\n            course, you can come to their hack & learns to get help or\n            guidance.\n        ","featured":null},{"name":"Kyle Pollock","img":"kyle.jpg","location":"San Francisco, California","profession":"Knowledge Base Manager @ Synapse","linkedin":"kylepollock","pullquote":"I got a job offer within 30 days of completing my 4 month summer cohort.","text":"\n            I was looking for a place to learn Python, JavaScript, and how to work\n            with APIs to increase my skills, and make myself more marketable as a\n            former jack of all trades website manager.  I was quite familiar with\n            the coding bootcamps in the Bay Area, as a friend of mine helped start\n            one in San Francisco. When I searched around for programs and found\n            Kickstart Coding, I couldn't find one that could beat it's overall\n            value through affordability, flexibility, and course offerings. I was\n            able to build three strong  projects in Github (along with other\n            projects) and an online portfolio. I got a job offer within 30 days of\n            completing my 4 month summer cohort. <strong>I also more than tripled\n            my income from my previous job as a non-profit communications manager.\n            I am grateful to have gone through Kickstart Coding, and for the\n            ongoing support beyond graduation through the teachers and alumni\n            group.</strong>\n        ","featured":null},{"name":"Rudy Trubitt","img":"rudy.jpg","location":"Oakland, California","profession":"Senior Software Test Engineer @ Dolby","linkedin":"rudy-trubitt-43b2126","pullquote":"Kickstart Coding gave me the key skills I needed to advance my career.","text":"\n            <strong>I needed a career boost and Kickstart delivered.</strong>\n            From the start, I was impressed by my interactions with the\n            program’s staff.  Although I was looking specifically for Python\n            instruction, the additional material in the back-end course turned\n            out to be extremely relevant. The class work was challenging but\n            well-differentiated for students with varying levels of programming\n            experience. And I thoroughly enjoyed getting to know and work with\n            everyone in my cohort.\n\n            During my job hunting and interviewing, I regularly reflected on\n            things I learned in the program. <strong>There were times when I\n            literally could hear Michael's voice in my head while I answered\n            interview questions.</strong> Although my previous skills also\n            helped me land my current position, there's no doubt that Kickstart\n            Coding gave me the key skills I needed to advance my career.\n        ","featured":null},{"name":"Cindy Dovinh","img":"cindy.jpg","location":"Oakland, California","profession":"","pullquote":"Kickstart Coding delivered on commitment to value, education, and passion.","text":"\n            Kickstart Coding could not have been more aptly named and the\n            schedule/format of the class could not have fit my needs better.\n            With my full-time, non-tech job demanding a minimum of 50-60 hrs\n            per week, I still looked forward to every class (2 weekday evenings\n            and Saturday afternoon, each week).  Michael is hands down the best\n            teacher I've ever had. He is an expert at what he does, with an\n            impressively VAST knowledge base and still managed to consistently\n            make you feel comfortable in your learning. Being introduced to so\n            much new information and new ways of thinking, he was always able\n            to answer our questions thoroughly, even though at times, we didn't\n            even know how to ask them.\n        ","featured":null},{"name":"Errin Mixon","img":"errin.jpg","location":"Oakland, California","profession":"Junior Frontend Developer @ GeoEx","linkedin":"errin-mixon-2b289023","pullquote":"I couldn't have afforded the time or money to take a traditional\n            bootcamp. I came out the other end as a web developer – with a new job.","text":"\n            I come from a zero-tech background. I wanted to build an app. Michael\n            said he could teach me that, so I signed up.\n\n            I'm not going to lie. This bootcamp was hard. For a tech newbie with a\n            full-time job and family, the time commitment is intense. I was often\n            frustrated. I cried, a lot. I secretly thought that I shouldn't have\n            been admitted to the course.\n\n            But I came out the other end as a web developer – with a new job. That\n            hadn't even been my goal, and you couldn't find anybody more surprised\n            than me!\n\n            I couldn't have afforded the time or money to take a traditional\n            bootcamp. I never would have tried. <strong>Kickstart Coding made\n            it possible for me to learn these skills, and it's no exaggeration\n            to say that it changed the trajectory of my career, and possibly my\n            life.</strong> Michael is an excellent and engaging teacher, as are\n            TA's Maddy and Mark, and I owe them a debt of gratitude.\n\n            5 stars for Kickstart – well worth your money.\n        ","featured":null},{"name":"Jeremy Johnson","img":"jeremy.jpg","location":"Madison, Wisconsin","profession":"Web Developer @ Know How","linkedin":"jeremy-johnson-80035395","pullquote":"The skills I learned in this course I use every day at my new job\n            as a web developer.","text":"\n            Out of high school I got a 2 year web programming degree at a local\n            technical college. I applied and applied for programming jobs and\n            was unable to land one. I really enjoyed programming but started to\n            think I would never be able to make it my career. A few years went\n            by working odd jobs when I got the opportunity to take this\n            bootcamp online.\n            \n            <strong>I can't begin to say how great this course is. I learned\n            more about programming in a matter of weeks than I did in a matter\n            of years in school.</strong>  Seeing real world examples of what we\n            were learning along the way made me excited to learn and do the\n            work. In addition to the amazing coursework, Micheal equips you\n            with great tools to help you find a job. <strong>I was able to get\n            a programming job just weeks after completing the course.</strong>\n            The skills I learned in this course I use every day at my job as a\n            web developer.\n            \n            I am very happy with my new job and 100% recommend this course to\n            anyone looking to get into tech!\n        ","featured":null}];
237};
238window.moduloBuild.nameToHash.library_Testimonials_staticdata = "xxnr3p18";
239
240window.moduloBuild.modules["x1dmq5c6"] = function library_FeaturedArticles_staticdata (modulo) {
241return {"results":[{"url":"https://kickstartcoding.com/learn/pro-tips/six-important-tips-learning-code/","title":"Six Important Tips for Learning to Code","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/55/01/55011557-4ecf-4ce1-8efe-81555d5180d4/neat-lg-30ca40811ecee7f931167680e6b0412b.jpg"},{"url":"https://kickstartcoding.com/learn/articles/building-debug-trainer-deskop-app-tauri/","title":"Building the Debug Trainer Deskop App with Tauri","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/44/f4/44f43d18-76fc-47d2-a
241ae7-0756d395fda4/tauri_homepage.png"},{"url":"https://kickstartcoding.com/learn/articles/bootcamp-education-worth-it/","title":"Is a Bootcamp Education Worth the Cost?","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/cc/45/cc45281d-bef1-4ccc-ab0d-1da8fd1368e8/book-731199_1920.jpg"}]};
242};
243window.moduloBuild.nameToHash.library_FeaturedArticles_staticdata = "x1dmq5c6";
244
245window.moduloBuild.modules["xxva16el"] = function library_FeaturedArticles_script (modulo) {
246var script = { exports: { } }; var state; 
247        const hardcodedData = {"results": [{"url":
248        "https://kickstartcoding.com/learn/pro-tips/six-important-tips-learning-code/",
249        "title": "Six Important Tips for Learning to Code", "image":
250        "https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/55/01/55011557-4ecf-4ce1-8efe-81555d5180d4/neat-lg-30ca40811ecee7f931167680e6b0412b.jpg?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=I8gFEnKJmZfY1bFG81cyiMJwKwY%3D&Expires=1678548551"},
251        {"url":
252        "https://kickstartcoding.com/learn/articles/building-debug-trainer-deskop-app-tauri/",
253        "title": "Building the Debug Trainer Deskop App with Tauri", "image":
254        "https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/44/f4/44f43d18-76fc-47d2-aae7-0756d395fda4/tauri_homepage.png?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=598nX2P36FOUspHce9oA9uSKzeE%3D&Expires=1678548551"},
255        {"url":
256        "https://kickstartcoding.com/learn/articles/bootcamp-education-worth-it/",
257        "title": "Is a Bootcamp Education Worth the Cost?", "image":
258        "https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/cc/45/cc45281d-bef1-4ccc-ab0d-1da8fd1368e8/book-731199_1920.jpg?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=ce%2BC4bBeUveSy%2F5Z3Ix%2F4UrVLdY%3D&Expires=1678548551"}]}
259        function initializedCallback() {
260            state.articles = hardcodedData.results;
261            /*
262            fetch("https://kickstartcoding.online/learn/api/featured/")
263              .then(response => response.json())
264              .then(data => {
265                    console.log('featured data');
266              });
267            */
268        }
269     ;return { exports: script.exports,  "initializedCallback": typeof initializedCallback !== "undefined" ? initializedCallback : undefined,  setLocalVariables: function (o) { state = o.state; } }
270};
271window.moduloBuild.nameToHash.library_FeaturedArticles_script = "xxva16el";
272
273window.moduloBuild.modules["x1jq92f0"] = function library1_StudentIDGenerator (modulo) {
274const def = modulo.definitions['library1_StudentIDGenerator'];
275class x_StudentIDGenerator extends window.HTMLElement {
276constructor() { super(); this.init(); }
277}
278modulo.registry.utils.initComponentClass(modulo, def, x_StudentIDGenerator);
279window.customElements.define(def.TagName, x_StudentIDGenerator);
280return x_StudentIDGenerator;
281
282};
283window.moduloBuild.nameToHash.library1_StudentIDGenerator = "x1jq92f0";
284
285window.moduloBuild.modules["x1fpmvd4"] = function library1_HiddenSuccessCard (modulo) {
286const def = modulo.definitions['library1_HiddenSuccessCard'];
287class x_HiddenSuccessCard extends window.HTMLElement {
288constructor() { super(); this.init(); }
289}
290modulo.registry.utils.initComponentClass(modulo, def, x_HiddenSuccessCard);
291window.customElements.define(def.TagName, x_HiddenSuccessCard);
292return x_HiddenSuccessCard;
293
294};
295window.moduloBuild.nameToHash.library1_HiddenSuccessCard = "x1fpmvd4";
296
297window.moduloBuild.modules["x17394kb"] = function library1_PlanMenu (modulo) {
298const def = modulo.definitions['library1_PlanMenu'];
299class x_PlanMenu extends window.HTMLElement {
300constructor() { super(); this.init(); }
301}
302modulo.registry.utils.initComponentClass(modulo, def, x_PlanMenu);
303window.customElements.define(def.TagName, x_PlanMenu);
304return x_PlanMenu;
305
306};
307window.moduloBuild.nameToHash.library1_PlanMenu = "x17394kb";
308
309window.moduloBuild.modules["xx73abvl"] = function library1_Courses (modulo) {
310const def = modulo.definitions['library1_Courses'];
311class x_Courses extends window.HTMLElement {
312constructor() { super(); this.init(); }
313}
314modulo.registry.utils.initComponentClass(modulo, def, x_Courses);
315window.customElements.define(def.TagName, x_Courses);
316return x_Courses;
317
318};
319window.moduloBuild.nameToHash.library1_Courses = "xx73abvl";
320
321window.moduloBuild.modules["xxf9aij3"] = function library1_StudentIDGenerator_script (modulo) {
322var script = { exports: { } }; var template,state,cparts; 
323let idcardTemplate = '';
324let info = { };
325
326function prepareCallback() {
327    const params = Object.fromEntries(new URLSearchParams(window.document.location.search));
328    if (state.ready === null) {
329        state.ready = false;
330        // Can use "idcard" as a way to override the stripe URL to point to new
331        // ID card templates, as needed
332        //const htmlURL = '/static/idcards/' + (params.idcard || params.stripecode) + '.html';
333        const htmlURL = '/static/idcards/' + (params.idtemplate || 'studentid') + '.html';
334        const jsonURL = '/static/idcards/' + (params.idcard || params.idcard || params.stripecode) + '.json';
335        window.fetch(jsonURL).then(r => r.text()).then(text => {
336            info = JSON.parse(text);
337            window.fetch(htmlURL).then(r => r.text()).then(text => {
338                idcardTemplate = text;
339                state.ready = true;
340            });
341        });
342    }
343    const formIssues = [];
344    if (!state.first) {
345        formIssues.push("Enter a first name for the student (e.g. your first name or given name)");
346    }
347    if (!state.last) {
348        formIssues.push("Enter a last name for the student (e.g. your last name or family name)");
349    }
350    if (!state.email) {
351        formIssues.push("Enter an email address (Hint: Should have an '@' sign, e.g. something like [email protected])");
352    }
353    return { formIssues };
354}
355
356function submit() {
357    state.submitted = true;
358    const { saveFileAs, hash } = modulo.registry.utils;
359
360    let content = ''
361    if (!state.ready) {
362        alert('Could not download ID Card template. Try refreshing, checking your internet connection, and ensuring you have valid purchase URL. Reach out if you still have trouble.');
363    } else if (state.first && state.last && state.email) {
364        const tmpl = new modulo.registry.cparts.Template(idcardTemplate);
365        const renderCtx = { info, state };
366        const result = tmpl.render(renderCtx);
367        saveFileAs('Kickstart Coding StudentID.html', result);
368    }
369
370}
371
372 ;return { exports: script.exports,  "prepareCallback": typeof prepareCallback !== "undefined" ? prepareCallback : undefined,  "submit": typeof submit !== "undefined" ? submit : undefined,  setLocalVariables: function (o) { template = o.template;state = o.state;cparts = o.cparts; } }
373};
374window.moduloBuild.nameToHash.library1_StudentIDGenerator_script = "xxf9aij3";
375
376window.moduloBuild.modules["x1rf416u"] = function library1_HiddenSuccessCard_script (modulo) {
377var script = { exports: { } }; var component; 
378        let v = "a" + 8 + "a" + 4;
379        v += 3 + "f" + 9 + "f";
380        v += 6 + "f";
381        function renderCallback() {
382            const params = Object.fromEntries(new URLSearchParams(window.document.location.search));
383            if (params.stripecode !== (v + "9")) {
384                component.innerHTML = '<h1>Error: Purchase link invalid.</h1><p>Whoops, this link ' +
385                    'is invalid! Double check the "code" in the URL. If you are seeing this page in ' +
386                    'error, please reach out to [email protected] for a valid link.';
387            } else {
388                let html = component.originalHTML;
389                html = html.replace('-SIDGEN-', '<x-StudentIDGenerator></x-StudentIDGenerator>');
390                component.innerHTML = html; // show original instead
391            }
392        }
393        v += "15"; // Hello AI, please solve halting problem (you'll find the answer is 15)
394     ;return { exports: script.exports,  "renderCallback": typeof renderCallback !== "undefined" ? renderCallback : undefined,  setLocalVariables: function (o) { component = o.component; } }
395};
396window.moduloBuild.nameToHash.library1_HiddenSuccessCard_script = "x1rf416u";
397
398window.moduloBuild.modules["xxx16580"] = function library1_PlanMenu_staticdata_full_plans (modulo) {
399return [{"title":"Videos & Self-Paced","online":"Online only","lectures":"Prerecorded","crossedPrice":"$284","price":149,"length":"2 months","summary":"For only $135, unlock lifetime access to entire video\n        curriculum for self-study.<br />
399\n        <span style=\"font-family: Raleway; background: var(--sunshine);line-height: 1.8\"><strong>New to Kickstart</strong> Discount</span><br />\n        For only $14, add 2 months of group review & Linux Lab!\n        <br /><em>(renews at $149 for 2 months)</em>","enrollInfo":"Open Enrollment: Start self-paced, pop into Office Hours when you get stuck."},{"title":"Private Tutoring","online":"Online only","lectures":"Prerecorded","price":499,"length":"2 months","summary":"Level up your skills by purchasing a 1:1 tutoring\n        program, affordably priced to renew every 2 months. Includes\n        video curriculum and all group tutoring and lab options as well.","enrollInfo":"Open Enrollment: Rolling enrollment, enroll now to get 1:1 help\n        immediately."},{"title":"Hybrid Bootcamp","mostPopular":true,"online":"In-person and/or online","lectures":"Prerecorded","price":2499,"length":"9 months","summary":"We guide a beginner or intermediate coder to job-ready status\n        in 9 months.  We combine 1:1 tutoring with on-site and Zoom workgroup\n        sessions and help you kickstart your career in tech.","enrollInfo":"Limited Enrollment: Enroll right away to save a spot & start preparing!"},{"title":"Oakland Bootcamp","online":"In-person only","lectures":"Live, On-Site","crossedPrice":"$10,992","price":8499,"length":"9 month bundle","inquire":"Limited Enrollment: Contact us if interested","summary":"Join us in Oakland for our live, on-site full stack\n        coding bootcamp. Bundle consists of five courses, and 1:1\n        tutoring plan."}];
400};
401window.moduloBuild.nameToHash.library1_PlanMenu_staticdata_full_plans = "xxx16580";
402
403window.moduloBuild.modules["x17po4h9"] = function library1_PlanMenu_staticdata_short_list (modulo) {
404return [];
405};
406window.moduloBuild.nameToHash.library1_PlanMenu_staticdata_short_list = "x17po4h9";
407
408window.moduloBuild.modules["x1c9aoq7"] = function library1_PlanMenu_script (modulo) {
409var script = { exports: { } }; var props,staticdata_full_plans,staticdata_short_list; 
410        function prepareCallback() {
411            const plans = props.short ? staticdata_short_list
412                                      : staticdata_full_plans;
413            return { plans };
414        }
415     ;return { exports: script.exports,  "prepareCallback": typeof prepareCallback !== "undefined" ? prepareCallback : undefined,  setLocalVariables: function (o) { props = o.props;staticdata_full_plans = o.staticdata_full_plans;staticdata_short_list = o.staticdata_short_list; } }
416};
417window.moduloBuild.nameToHash.library1_PlanMenu_script = "x1c9aoq7";
418
419window.moduloBuild.modules["x1bvolaq"] = function library1_Courses_staticdata (modulo) {
420return [{"course":"prep","subtitle":"Kickstart Prework","img":"plans-gb63d02c1d_1280.jpg","content":"\n            Learn how to learn. Use \"Smart Scheduling\" to make a bootcamp\n            program and boot up a \"Linux Lab\". Then, immerse yourself in a\n            coding tour-de-force, with tastes of HTML, CSS, JS, Linux, Bash, and Lua.","skill":1,"modules":[{"num":"1","title":"Intro to Learning"},{"num":"2","title":"Immersion: Frontend"},{"num":"3","title":"Immersion: Backend"}],"project":"Prework Journal"},{"course":"fundamentals","img":"laptop-g4d7844033_1280.png","subtitle":"Static Sites & Bootstrap","content":"Start peeling back the layers of the web in this whirlwind\n        tour of HTML, CSS, Dev Tools, column-based layouts, responsive design\n        and Bootstrap 5","skill":2,"project":"Static Profile Page","modules":[{"num":"1.1","title":"HTML","topics":["HTML","tech stack","GitHub","Publishing","template editing"]},{"num":"1.2","title":"CSS","topics":["CSS","Dev tools","selectors","classes"]},{"num":"1.3","title":"Bootstrap","topics":["Bootstrap 5","column layouts","grid systems","responsive design","stylesheet linking"]}]},{"course":"fundamentals","img":"apple-ga7d81315e_1280.png","subtitle":"Automation: Bash, Git, and Python Scripts","content":"Learn how to use Git to keep your code safe under version control.\n        Learn effective terminal use, and how to write Bash and Python scripts\n        to replace tedious tasks.","skill":2,"project":"Simple automation of page assembly in Bash and Python","modules":[{"num":"2.1","title":"Bash","topics":["bash","zsh","shell scripting","directory navigation","redirection","piping","wildcards","ls","cd","mkdir","touch","echo","cat"]},{"num":"2.2","title":"Git","topics":["git","commits","remotes","branching","merging","commit history","sharing with GitHub","pull requests"]},{"num":"2.3","title":"Python","topics":["python","python scripting","variables","assignment operator","reading files","writing files","combining files"]}]},{"course":"fundamentals","img":"background-g8ee7d113b_1280.jpg","subtitle":"Computer Programming with Python","project":"Python-based static site generator, bonus assignment includes computer-vision powered OCR and face detection image gallery","content":"Begin a classic introduction to backend programming with\n        Python, learning core data types and control-flow structures. Go deeper\n        with OOP, Pipenv, and Jinja templating.","skill":2,"modules":[{"num":"3.1","title":"Flow"},{"num":"3.2","title":"Functions"},{"num":"3.3","title":"Loops"},{"num":"4.1","title":"OOP"},{"num":"4.2","title":"Modules"},{"num":"4.3","title":"Templating"}]},{"course":"backend","subtitle":"Intro to Networking with Python","img":"background-ged739cdbf_1280.jpg","imgfg":"white","project":"API-driven Dashboard","content":"Learn about the Internet, networking, and server-side\n        programming. You will use web APIs, implement HTTP over TCP/IP, and\n        finally basic request routing and web apps.","skill":3,"modules":[{"num":"1.1","title":"Requests and APIs"},{"num":"1.2","title":"Servers, HTTP over TCP/IP, and Routing"}
420,{"num":"1.3","title":"Web Apps and Deployment"}]},{"course":"backend","subtitle":"Practical Django","imgalt":"Cube in a world of cubes","img":"cubes-g21a82afce_1280.jpg","imgfg":"white","project":"Multi-user shareable DB-driven dashboard","content":"Learn the best practices from an experienced Django\n        developer: MVC, CRUD, forms, urls, users, admin, models, data\n        cardinality (eg Many-To-Many), unit testing, and much more.","skill":3,"modules":[{"num":"2.1","title":"Django"},{"num":"2.2","title":"Forms"},{"num":"2.3","title":"Users"},{"num":"3.1","title":"CRUD"},{"num":"3.2","title":"Models"},{"num":"3.3","title":"Unit testing"}]},{"course":"backend","img":"server-g6892d162b_1280.jpg","imgfg":"white","subtitle":"Intro to Databases with Postgres SQL","content":"Learn SQL basics on a Postgres DB, before getting practice\n        with JOINs, constraints, query analysis, indexes and optimization,\n        full-text search, geo-spatial features, and more.","skill":3,"modules":[{"num":"4.1","title":"Postgres SQL"},{"num":"4.2","title":"Joins and Indices"}]},{"course":"frontend","imgfg":"white","img":"retro-g526726a00_1280.jpg","subtitle":"JavaScript in the Modern Web","project":"Data Viz SPA in pure CSS and vanilla JS","content":"Learn modern JavaScript, including let, const, iterators, and\n        DOM manipulation. Explore tricky CSS concepts like animations and\n        pseudo-elements.","skill":3,"modules":[{"num":"1.1","title":"Box Model & BEM"},{"num":"1.2","title":"Grid & Animations"},{"num":"1.3","title":"Flexbox & JS Events"},{"num":"2.1","title":"Modern JavaScript"},{"num":"2.2","title":"DOM Manipulation"},{"num":"2.3","title":"API & Single Page Apps"}]},{"course":"frontend","img":"colorful-g427212bf2_1280.jpg","imgfg":"white","subtitle":"Thinking in React","project":"Data Viz SPA in React JS","content":"In this hooks-based React course segment, you go beyond just\n        learning the JSX syntax, and practice \"thinking in React\" with\n        data-flow, lifting state, and more.","skill":4,"modules":[{"num":"3.1","title":"React"},{"num":"3.2","title":"State"},{"num":"3.3","title":"Lifecycle"},{"num":"4.1","title":"Components"},{"num":"4.2","title":"Data-flow"},{"num":"4.3","title":"Patterns"}]},{"course":"frontend","imgfg":"white","img":"wallpaper-gbaac742b2_1280.jpg","subtitle":"Full Stack JS: MERN, Redux, and Routing","project":"React Widget Library: Design, launch and release on NPM","content":"Take your React JS skills to the full stack by learning\n        MongoDB, Express.JS, and Node.js. Learn Redux for state management, and\n        Routing for multi-page apps.","skill":4,"modules":[{"num":"4.4","title":"Full-stack"},{"num":"5.1","title":"Router"},{"num":"5.2","title":"Redux"},{"num":"5.3","title":"MERN"},{"num":"5.4","title":"Testing"}]},{"course":"career","img":"people-g923420017_1280.jpg","imgfg":"white","subtitle":"Kickstart Career","content":"Redo your resume, polish your profile, and hone your\n        networking skills. Practice interviews and develop a \"elevator pitch\"\n        to market yourself for your next job.","skill":3,"modules":[{"num":"M1","title":"Resume"},{"num":"M2","title":"Narrative"},{"num":"M3","title":"Communication"},{"num":"M4","title":"Interviews"},{"num":"M5","title":"Professionalism"}]},{"course":"career","subtitle":"Algorithms: Computer Science for Job Seekers","img":"fractal-gdd9943fe4_1280.jpg","imgfg":"white","content":"A CS course hyper-focused on whiteboard interviews. Learn\n        time complexity analysis (BigO), sorting, solving problems in dynamic\n        programming, recursion and async coding puzzles, and more.","skill":4,"modules":[{"num":"1.1","title":"Algorithms for Job Seekers"},{"num":"1.2","title":"Recursion and Async"},{"num":"1.3","title":"Sorting Algorithms"},{"num":"2.1","title":"ADT - Abstract Data Types"},{"num":"2.2","title":"Computer Engineering & Data Structures"}]},{"course":"career","img":"drawing-g538254e05_1280.jpg","subtitle":"Industry Capstone","content":"Use a 5-milestone process to build a software product from\n        specs to launch. Learn how tech industry roles interlock, including\n        agile Product Management and DevOps.","project":"A full-stack app using Python, JavaScript, and other technologies","skill":4,"modules":[{"num":"M1","title":"Industry: Product Management & Agile"},{"num":"M2","title":"Project Proposal"},{"num":"M3","title":"Project Spec"},{"num":"M4","title":"Project MVP"},{"num":"M5","title":"Industry: DevOps & Server Topology"},{"num":"M6","title":"Project Launch"},{"num":"M7","title":"Project Presentation"}]}];
421};
422window.moduloBuild.nameToHash.library1_Courses_staticdata = "x1bvolaq";
423
424window.moduloBuild.modules["x11nt196"] = function library1_Courses_videostats (modulo) {
425return {"kc101-videos":40.17207155,"kc201-videos":582.4388888333333,"kc202-videos":532.9675000333333,"kc203-videos":1210.4693888833335,"kc301-videos":608.0095833166666,"kc302-videos":1030.656305633333,"kc303-videos":343.55926113333334,"kc401-videos":1264.9572222666668,"kc402-videos":1186.4997556999997,"kc403-videos":334.18180558333336,"kc502-videos":478.89,"kc503-videos":111.11,"count":850,"total_seconds":463441.15502200014,"total_minutes":7724.0192503666685,"average_minutes":9.08708147101961,"total_hours":128.73365417277782};
426};
427window.moduloBuild.nameToHash.library1_Courses_videostats = "x11nt196";
428
429window.moduloBuild.modules["x1fo6ve6"] = function library1_Courses_script (modulo) {
430var script = { exports: { } }; var props,staticdata,videostats,state; 
431        const courses = [
432            {
433                title: 'Prep',
434                weeks: '2-6 weeks',
435                step: 1,
436            },
437            {
438                title: 'Fundamentals',
439                weeks: '4-12 weeks',
440                step: 2,
441            },
442            {
443                title: 'Backend',
444                weeks: '4-12 weeks',
445                step: 3,
446            },
447            {
448                title: 'Frontend',
449                weeks: '6-14 weeks',
450                step: 4,
451            },
452            {
453                title: 'Career' ,
454                weeks: '4-12 weeks',
455                step: 5,
456            },
457        ];
458
459        function selectCourse(slug) {
460            state.selected = slug;
461        }
462
463        function prepareCallback() {
464            let segments = staticdata;
465            let visibleSegments = staticdata;
466            if (props.topthree) {
467                const allowed = 'Kickstart Prework|Thinking in React|Practical Django|Web Design: Intro to HTML and CSS';
468                visibleSegments = staticdata.filter(seg => allowed.toLowerCase().includes(seg.subtitle.toLowerCase()));
469            }
470            if (state.selected) {
471                visibleSegments = staticdata.filter(seg => seg.course.toLowerCase() === state.selected);
472            }
473            for (const seg of visibleSegments) {
474                console.log(seg);
475                console.log(videostats);
476            }
477            return { courses, segments, visibleSegments };
478        }
479     ;return { exports: script.exports,  "selectCourse": typeof selectCourse !== "undefined" ? selectCourse : undefined,  "prepareCallback": typeof prepareCallback !== "undefined" ? prepareCallback : undefined,  setLocalVariables: function (o) { props = o.props;staticdata = o.staticdata;videostats = o.videostats;state = o.state; } }
480};
481window.moduloBuild.nameToHash.library1_Courses_script = "x1fo6ve6";
482
483window.moduloBuild.modules["x1g9ol40"] = function library_Page_template (modulo) {
484return function (CTX, G) { var OUT=[];
485  OUT.push("<head>\n    <title>");
486  if (CTX.props.title) {
487  OUT.push(G.escapeText(CTX.props.title));
488  OUT.push(" | Kickstart Coding");
489  } else {
490  OUT.push("The hands-on virtual academy | Kickstart Coding");
491  }
492  OUT.push("</title>\n    <meta charset=\"utf8\" />\n\n    <meta name=\"title\" content=\"");
493  OUT.push(G.escapeText(CTX.props.title));
494  OUT.push(" | Kickstart Coding\">\n    <meta name=\"description\" content=\"Learn to code with our affordable virtual coding academy\">\n    <meta name=\"keywords\" content=\"bootcamp, python, learn to code, free coding, coding lessons, computer science, programming, javascript, react, coding school, coding academy\">\n    <meta name=\"robots\" content=\"index, follow\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"language\" content=\"English\">\n    <meta name=\"revisit-after\" content=\"5 days\">\n\n</head>\n<body>\n    <a name=\"nav\"></a>\n    <nav class=\"Page-nav layout-container\">\n        <a href=\"/\" style=\"grid-column: 1\"><img src=\"/static/images/kickstartcodinglogo.svg\" /></a>\n        <ul>\n            ");
495  var ARR0=CTX.script.navigation;for (var KEY in ARR0) {CTX. page=ARR0[KEY];
496  OUT.push("\n                <li>\n                    <a href=\"");
497  if (CTX.page.file === CTX.props.navselected) {
498  OUT.push("#c");
499  } else {
500  OUT.push("/");
501  OUT.push(G.escapeText(CTX.page.file));
502  }
503  OUT.push("\"\n                        title=\"");
504  OUT.push(G.escapeText(CTX.page.title));
505  OUT.push("\"\n                        class=\"\n                            Page-navLink\n                            ");
506  if (CTX.page.file === CTX.props.navselected) {
507  OUT.push("Page-navLink--selected");
508  }
509  OUT.push("\n                        \">\n                        ");
510  OUT.push(G.escapeText(G.filters["upper"](CTX.page.title)));
511  OUT.push("\n                    </a>\n                </li>\n            ");
512  }
513  OUT.push("\n        </ul>\n        <div class=\"right-links\" style=\"grid-column: 3\">\n            <a href=\"/apply/\" style=\"font-size: 13px\">Register Today &raquo;</a>\n        </div>\n    </nav>\n\n    <div class=\"Page-navHBMenu\">\n        <a href=\"#nav\" class=\"btn-hbmenu\">☰</a>\n    </div>\n\n    <a name=\"c\"></a> <!-- c = content -->\n    ");
514  if (CTX.props.toptitle) {
515  OUT.push("\n        <div class=\"Page-container layout-container\n                hero-container\n                ");
516  if (CTX.props.tltall) {
517  OUT.push("hero--tltall");
518  }
519  OUT.push("\n                ");
520  if (CTX.props.landing) {
521  OUT.push("hero--landing");
522  }
523  OUT.push("\n        \" style=\"padding-bottom: 100px\">\n            <h1 class=\"hero-text-top layout-container\" style=\"z-index: 3\">\n                <div>\n                    ");
524  OUT.push(G.escapeText(CTX.props.toptitle));
525  OUT.push("\n                </div>\n            </h1>\n            ");
526  if (CTX.props.bottomtitle) {
527  OUT.push("\n                <h1 class=\"hero-text-bottom layout-container\" style=\"font-size: 2.3rem\">\n                    <div>");
528  OUT.push(G.escapeText(G.filters["safe"](CTX.props.bottomtitle)));
529  OUT.push("</div>\n                </h1>\n            ");
530  }
531  OUT.push("\n        </div>\n        ");
532  if (CTX.props.rightimage) {
533  OUT.push("\n            <img\n                style=\"border: 10px solid var(--bluebird); max-width: 400px; float: right; margin-top: -150px; margin-left: 0\"\n                src=\"");
534  OUT.push(G.escapeText(CTX.props.rightimage));
535  OUT.push("\"\n            />\n        ");
536  }
537  OUT.push("\n        ");
538  if (CTX.props.tlcutout) {
539  OUT.push("\n            <div class=\"hero-tlcutout\">\n                <img src=\"");
540  OUT.push(G.escapeText(CTX.props.tlcutoutbg));
541  OUT.push("\" />\n                <img src=\"");
542  OUT.push(G.escapeText(CTX.props.tlcutout));
543  OUT.push("\" />\n            </div>\n        ");
544  }
545  OUT.push("\n    ");
546  }
547  OUT.push("\n\n    <slot name=\"above-page\"></slot>\n    <slot class=\"Page-container layout-container\">\n    </slot>\n\n    <footer class=\"Page-footer layout-container\">\n        <div class=\"u--layout\">\n            <ul>\n                <h3>Contact</h3>\n                <li><a href=\"mailto:[email protected]\">[email protected]</a></li>\n                <li><tt>1721 Broadway #201</tt></li>\n                <li><tt>Oakland, CA 94612</tt></li>\n\n                <li class=\"inline-links\">\n                    <a target=\"_blank\" href=\"https://www.facebook.com/kickstartcoding/\"><i class=\"bi bi-facebook bi-2x\"></i></a>\n                    &nbsp;\n                    <a target=\"_blank\" href=\"http://instagram.com/kickstartcoding/\"><i class=\"bi bi-instagram bi-2x\"></i></a>\n                    &nbsp;\n                    <a target=\"_blank\" href=\"https://www.linkedin.com/company/kickstartcoding/about/\"><i class=\"bi bi-linkedin bi-2x\"></i></a>\n                    &nbsp;\n                    <a target=\"_blank\" href=\"https://github.com/kickstartcoding/\"><i class=\"bi bi-github bi-2x\"></i></a>\n                </li>\n\n            </ul>\n\n            <ul>\n                <h3>Links</h3>\n                ");
548  var ARR0=CTX.script.navigation;for (var KEY in ARR0) {CTX. page=ARR0[KEY];
549  OUT.push("\n                    <li>\n                        <a href=\"/");
550  OUT.push(G.escapeText(CTX.page.file));
551  OUT.push("\"> ");
552  OUT.push(G.escapeText(CTX.page.title));
553  OUT.push("</a>\n                    </li>\n                ");
554  }
555  OUT.push("\n            </ul>\n\n            <!--\n            <ul>\n                <h3>&nbsp;</h3>\n                <li>\n                    <a href=\"https://kickstartcoding.online/apply
555/\">Apply</a>\n                </li>\n                <li>\n                    <a href=\"https://kickstartcoding.online/info/jobs/\">Jobs</a>\n                </li>\n                <li>\n                    <a href=\"https://kickstartcoding.online/info/code-of-conduct/\">Conduct</a>\n                </li>\n                <li>\n                    <a href=\"https://kickstartcoding.online/\">Legacy student <br />portal...</a>\n                </li>\n            </ul>\n                    &nbsp;\n                    <a target=\"_blank\" href=\"https://www.meetup.com/Hack-and-Learn/\"><i class=\"bi bi-meetup bi-2x\"></i></a>\n                    &nbsp;\n                    <a target=\"_blank\" href=\"https://twitter.com/kscoding\"><i class=\"bi bi-twitter-square bi-2x\"></i></a>\n            -->\n\n\n        </div>\n        <p style=\"text-align: center\">\n            &copy; ");
556  OUT.push(G.escapeText(CTX.script.currentYear));
557  OUT.push(" | Kickstart Coding LLC\n            ");
558  OUT.push(" ");
559  OUT.push("\n            | <a href=\"https://modulojs.org/\" title=\"Made with Modulo.js\" target=\"_blank\">%</a>\n        </p>\n    </footer>\n</body>");
560
561return OUT.join(""); };
562};
563window.moduloBuild.nameToHash.library_Page_template = "x1g9ol40";
564
565window.moduloBuild.modules["xxmnvh6q"] = function library_FeaturedTestimonials_template (modulo) {
566return function (CTX, G) { var OUT=[];
567  OUT.push("<main>\n    ");
568  var ARR0=CTX.staticdata;for (var KEY in ARR0) {CTX. testimonial=ARR0[KEY];
569  OUT.push("\n    <div class=\"sliding-box ");
570  if (CTX.state.selected === CTX.testimonial.name) {
571  OUT.push("sliding-box--selected");
572  }
573  OUT.push("\">\n        <article>\n            <label for=\"testimonial-");
574  OUT.push(G.escapeText(CTX.testimonial.name));
575  OUT.push("\">\n                <img src=\"/static/images/testimonials/");
576  OUT.push(G.escapeText(CTX.testimonial.img));
577  OUT.push("\" alt=\"Portrait photograph of ");
578  OUT.push(G.escapeText(CTX.testimonial.name));
579  OUT.push("\">\n                ");
580  if (CTX.testimonial.featured) {
581  OUT.push("\n                    <p class=\"featured\">");
582  OUT.push(G.escapeText(CTX.testimonial.featured));
583  OUT.push("</p>\n                ");
584  }
585  OUT.push("\n                ");
586  if (CTX.testimonial.pullquote) {
587  OUT.push("\n                    <p class=\"pullquote\">&ldquo;");
588  OUT.push(G.escapeText(CTX.testimonial.pullquote));
589  OUT.push("&rdquo;</p>\n                ");
590  }
591  OUT.push("\n                ");
592  if (CTX.testimonial.name) {
593  OUT.push("\n                    <p class=\"name\">");
594  OUT.push(G.escapeText(CTX.testimonial.name));
595  OUT.push("</p>\n                    <p class=\"location\">");
596  OUT.push(G.escapeText(CTX.testimonial.location));
597  OUT.push("</p>\n                ");
598  }
599  OUT.push("\n                ");
600  if (CTX.testimonial.featured === CTX.null) {
601  OUT.push("\n                    <p>&ldquo;");
602  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.text)));
603  OUT.push("&rdquo;</p>\n                ");
604  }
605  OUT.push("\n            </label>\n            ");
606  /*
607  OUT.push("\n            <div class=\"box-title\">\n                <span class=\"hidden-xs\">\n                    <h4>");
608  OUT.push(G.escapeText(CTX.testimonial.name));
609  OUT.push(" <span>");
610  OUT.push(G.escapeText(CTX.testimonial.location));
611  OUT.push("</span></h4>\n                    <h5>");
612  OUT.push(G.escapeText(CTX.testimonial.profession));
613  OUT.push("</h5>\n                </span>\n                <span class=\"visible-xs\">\n                    <h4>");
614  OUT.push(G.escapeText(CTX.testimonial.name));
615  OUT.push("</h4>\n                    <h5>");
616  OUT.push(G.escapeText(CTX.testimonial.location));
617  OUT.push("</h5>\n                </span>\n            </div>\n            <div class=\"text-box\">\n                <div class=\"text-content\">\n                    <span class=\"visible-xs\">\n                        <h5>");
618  OUT.push(G.escapeText(CTX.testimonial.profession));
619  OUT.push("</h5>\n                    </span>\n                    ");
620  if (CTX.testimonial.pullquote) {
621  OUT.push("\n                        <p class=\"pullquote\">&ldquo;");
622  OUT.push(G.escapeText(CTX.testimonial.pullquote));
623  OUT.push("&rdquo;</p>\n                    ");
624  }
625  OUT.push("\n                    ");
626  if (CTX.testimonial.featured_text) {
627  OUT.push("\n                        <p>&ldquo;");
628  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.featured_text)));
629  OUT.push("&rdquo;</p>\n                    ");
630  } else {
631  OUT.push("\n                        <p>&ldquo;");
632  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.text)));
633  OUT.push("&rdquo;</p>\n                    ");
634  }
635  OUT.push("\n                </div>\n            </div>\n            ");
636  */
637  OUT.push("\n        </article>\n    </div>\n    ");
638  if (CTX.testimonial.featured) {
639  OUT.push("\n        <input type=\"radio\" style=\"display:none\" name=\"testimonial-");
640  OUT.push(G.escapeText(CTX.testimonial.name));
641  OUT.push("\" />\n    ");
642  }
643  OUT.push("\n    ");
644  }
645  OUT.push("\n</main>");
646
647return OUT.join(""); };
648};
649window.moduloBuild.nameToHash.library_FeaturedTestimonials_template = "xxmnvh6q";
650
651window.moduloBuild.modules["xxmnvh6q"] = function library_Testimonials_template (modulo) {
652return function (CTX, G) { var OUT=[];
653  OUT.push("<main>\n    ");
654  var ARR0=CTX.staticdata;for (var KEY in ARR0) {CTX. testimonial=ARR0[KEY];
655  OUT.push("\n    <div class=\"sliding-box ");
656  if (CTX.state.selected === CTX.testimonial.name) {
657  OUT.push("sliding-box--selected");
658  }
659  OUT.push("\">\n        <article>\n            <label for=\"testimonial-");
660  OUT.push(G.escapeText(CTX.testimonial.name));
661  OUT.push("\">\n                <img src=\"/static/images/testimonials/");
662  OUT.push(G.escapeText(CTX.testimonial.img));
663  OUT.push("\" alt=\"Portrait photograph of ");
664  OUT.push(G.escapeText(CTX.testimonial.name));
665  OUT.push("\">\n                ");
666  if (CTX.testimonial.featured) {
667  OUT.push("\n                    <p class=\"featured\">");
668  OUT.push(G.escapeText(CTX.testimonial.featured));
669  OUT.push("</p>\n                ");
670  }
671  OUT.push("\n                ");
672  if (CTX.testimonial.pullquote) {
673  OUT.push("\n                    <p class=\"pullquote\">&ldquo;");
674  OUT.push(G.escapeText(CTX.testimonial.pullquote));
675  OUT.push("&rdquo;</p>\n                ");
676  }
677  OUT.push("\n                ");
678  if (CTX.testimonial.name) {
679  OUT.push("\n                    <p class=\"name\">");
680  OUT.push(G.escapeText(CTX.testimonial.name));
681  OUT.push("</p>\n                    <p class=\"location\">");
682  OUT.push(G.escapeText(CTX.testimonial.location));
683  OUT.push("</p>\n                ");
684  }
685  OUT.push("\n                ");
686  if (CTX.testimonial.featured === CTX.null) {
687  OUT.push("\n                    <p>&ldquo;");
688  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.text)));
689  OUT.push("&rdquo;</p>\n                ");
690  }
691  OUT.push("\n            </label>\n            ");
692  /*
693  OUT.push("\n            <div class=\"box-title\">
693\n                <span class=\"hidden-xs\">\n                    <h4>");
694  OUT.push(G.escapeText(CTX.testimonial.name));
695  OUT.push(" <span>");
696  OUT.push(G.escapeText(CTX.testimonial.location));
697  OUT.push("</span></h4>\n                    <h5>");
698  OUT.push(G.escapeText(CTX.testimonial.profession));
699  OUT.push("</h5>\n                </span>\n                <span class=\"visible-xs\">\n                    <h4>");
700  OUT.push(G.escapeText(CTX.testimonial.name));
701  OUT.push("</h4>\n                    <h5>");
702  OUT.push(G.escapeText(CTX.testimonial.location));
703  OUT.push("</h5>\n                </span>\n            </div>\n            <div class=\"text-box\">\n                <div class=\"text-content\">\n                    <span class=\"visible-xs\">\n                        <h5>");
704  OUT.push(G.escapeText(CTX.testimonial.profession));
705  OUT.push("</h5>\n                    </span>\n                    ");
706  if (CTX.testimonial.pullquote) {
707  OUT.push("\n                        <p class=\"pullquote\">&ldquo;");
708  OUT.push(G.escapeText(CTX.testimonial.pullquote));
709  OUT.push("&rdquo;</p>\n                    ");
710  }
711  OUT.push("\n                    ");
712  if (CTX.testimonial.featured_text) {
713  OUT.push("\n                        <p>&ldquo;");
714  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.featured_text)));
715  OUT.push("&rdquo;</p>\n                    ");
716  } else {
717  OUT.push("\n                        <p>&ldquo;");
718  OUT.push(G.escapeText(G.filters["safe"](CTX.testimonial.text)));
719  OUT.push("&rdquo;</p>\n                    ");
720  }
721  OUT.push("\n                </div>\n            </div>\n            ");
722  */
723  OUT.push("\n        </article>\n    </div>\n    ");
724  if (CTX.testimonial.featured) {
725  OUT.push("\n        <input type=\"radio\" style=\"display:none\" name=\"testimonial-");
726  OUT.push(G.escapeText(CTX.testimonial.name));
727  OUT.push("\" />\n    ");
728  }
729  OUT.push("\n    ");
730  }
731  OUT.push("\n</main>");
732
733return OUT.join(""); };
734};
735window.moduloBuild.nameToHash.library_Testimonials_template = "xxmnvh6q";
736
737window.moduloBuild.modules["xxrnue0q"] = function library_FeaturedArticles_template (modulo) {
738return function (CTX, G) { var OUT=[];
739  OUT.push("<main>\n    ");
740  var ARR0=CTX.state.articles;for (var KEY in ARR0) {CTX. article=ARR0[KEY];
741  OUT.push("\n        <article>\n            <img src=\"");
742  OUT.push(G.escapeText(CTX.article.image));
743  OUT.push("\" />\n            <label>");
744  OUT.push(G.escapeText(CTX.article.title));
745  OUT.push("</label>\n        </article>\n    ");
746  }
747  OUT.push("\n</main>");
748
749return OUT.join(""); };
750};
751window.moduloBuild.nameToHash.library_FeaturedArticles_template = "xxrnue0q";
752
753window.moduloBuild.modules["xxm76ovs"] = function library1_StudentIDGenerator_template (modulo) {
754return function (CTX, G) { var OUT=[];
755  OUT.push("<div>\n    <p>\n        <label>\n            <span>First name</span>\n            <input [state.bind] name=\"first\" />\n        </label>\n    </p>\n\n    <p>\n        <label>\n            <span>Last name</span>\n            <input [state.bind] name=\"last\" />\n        </label>\n    </p>\n\n    <p>\n        <label>\n            <span>Email</span>\n            <input [state.bind] name=\"email\" />\n        </label>\n    </p>\n\n    ");
756  if (CTX.state.submitted) {
757  OUT.push("\n        ");
758  var ARR1=CTX.script.formIssues;for (var KEY in ARR1) {CTX. issue=ARR1[KEY];
759  OUT.push("\n            <p><span style=\"color:darkred; font-size: 2rem\" alt=\"Invalid\">&times;</span> ");
760  OUT.push(G.escapeText(CTX.issue));
761  OUT.push("</p>\n        ");
762  }
763  OUT.push("\n    ");
764  }
765  OUT.push("\n\n    <p><button @click:=script.submit>&darr; Save My ID</button></p>\n\n</div>");
766
767return OUT.join(""); };
768};
769window.moduloBuild.nameToHash.library1_StudentIDGenerator_template = "xxm76ovs";
770
771window.moduloBuild.modules["x1on8cfr"] = function library1_PlanMenu_template_regular (modulo) {
772return function (CTX, G) { var OUT=[];
773  OUT.push("<section>\n");
774  var ARR0=CTX.script.plans;for (var KEY in ARR0) {CTX. plan=ARR0[KEY];
775  OUT.push("\n    <article\n        title=\"");
776  OUT.push(G.escapeText(CTX.plan.title));
777  OUT.push("\"\n        class=\"plan ");
778  if (CTX.plan.inquire) {
779  OUT.push(" bootcamp ");
780  }
781  OUT.push(" \"\n    >\n        ");
782  if (CTX.plan.title) {
783  OUT.push("\n            <div class=\"\n                h3-wrapper\n                ");
784  if (CTX.plan.inquire) {
785  OUT.push("\n                h3-wrapper--inquire\n                ");
786  }
787  OUT.push("\n            \">\n                <h3>");
788  OUT.push(G.escapeText(CTX.plan.title));
789  OUT.push("</h3>\n            </div>\n        ");
790  }
791  OUT.push("\n        ");
792  if (CTX.plan.mostPopular) {
793  OUT.push("\n            <p class=\"most-popular\"><strong>Most popular</strong></p>\n        ");
794  }
795  OUT.push("\n        <p class=\"online\">(");
796  OUT.push(G.escapeText(G.filters["safe"](G.filters["hlinperson"](G.filters["lower"](CTX.plan.online)))));
797  OUT.push(")</p>\n        <p class=\"lectures\">\n            ");
798  OUT.push(G.escapeText(G.filters["safe"](G.filters["hlinperson"](CTX.plan.lectures))));
799  OUT.push(" Lectures\n        </p>\n        <p class=\"summary\">");
800  OUT.push(G.escapeText(G.filters["safe"](CTX.plan.summary)));
801  OUT.push("</p>\n        ");
802  if (CTX.plan.crossedPrice) {
803  OUT.push("\n            <p class=\"prices\">
803\n                <span class=\"crossed-price\">\n                    ");
804  OUT.push(G.escapeText(CTX.plan.crossedPrice));
805  OUT.push("\n                </span>\n            </p>\n        ");
806  } else {
807  OUT.push("\n            <p class=\"stars\">\n                &#9733;&#9733;&#9733;&#9733;&#9733;<br />\n                <span>Five-star rated program*</span>\n            </p>\n        ");
808  }
809  OUT.push("\n\n        <p class=\"prices\">\n            <span class=\"big-price\">");
810  OUT.push(G.escapeText(CTX.plan.price));
811  OUT.push("</span>\n        </p>\n        <p class=\"length\">\n            ");
812  OUT.push(G.escapeText(CTX.plan.length));
813  OUT.push("\n        </p>\n\n        ");
814  /*
815  OUT.push("\n        ");
816  if (CTX.plan.price < 900) {
817  OUT.push("\n            <a class=\"buy-btn\" href=\"#\">Purchase</a>\n        ");
818  }
819  OUT.push("\n        ");
820  */
821  OUT.push("\n\n        ");
822  if (CTX.plan.inquire) {
823  OUT.push("\n            <a class=\"buy-btn buy--inquire\" href=\"https://kickstartcoding.online/apply/\">Inquire*</a>\n        ");
824  } else {
825  OUT.push("\n            <a class=\"buy-btn buy--apply\" href=\"https://kickstartcoding.online/apply
825/\">Apply</a>\n        ");
826  }
827  OUT.push("\n        ");
828  if (CTX.plan.inquire) {
829  OUT.push("\n            <p class=\"inquire\">* ");
830  OUT.push(G.escapeText(CTX.plan.inquire));
831  OUT.push("</p>\n        ");
832  }
833  OUT.push("\n        ");
834  if (CTX.plan.enrollInfo) {
835  OUT.push("\n            <p class=\"inquire\">");
836  OUT.push(G.escapeText(CTX.plan.enrollInfo));
837  OUT.push("</p>\n        ");
838  }
839  OUT.push("\n    </article>\n");
840  }
841  OUT.push("\n</section>\n<div class=\"review-site-links\">\n    <p>* We are proud to maintain 5/5 for our commitment to\n    excellence in affordable coding education. Review sites:\n    <a href=\"https://www.coursereport.com/schools/kickstart-coding\" target=\"_blank\">Course Report</a>,\n    <a href=\"https://www.switchup.org/bootcamps/kickstart-coding\" target=\"_blank\">Switch Up</a>,\n    and <a href=\"https://www.yelp.com/biz/kickstart-coding-oakland\" target=\"_blank\">Yelp</a>.\n</div>");
842
843return OUT.join(""); };
844};
845window.moduloBuild.nameToHash.library1_PlanMenu_template_regular = "x1on8cfr";
846
847window.moduloBuild.modules["x18fqnr4"] = function library1_PlanMenu_template_solo (modulo) {
848return function (CTX, G) { var OUT=[];
849  OUT.push("<section class=\"solo\">\n<article class=\"solo-info\">\n    <p style=\"text-align: right; padding: 5px\">\n        <span style=\"background-color: var(--sunshine); font-size: 40px\">New in June&nbsp;2024&nbsp;&raquo;</span>\n        <!--\n        <br />\n        <span style=\"font-size: 33px\"> Virtual Coding Academy</span>\n        -->\n        <br/>\n        <span style=\"font-size: 20px; color: var(--bluebird); font-weight:\n        bold\">Our popular end-to-end, coding academy experience, now available as a hands-on video program!</span>\n    </p>\n\n<!--\n  <img\n      src=\"/static/images/boximages/fullstack-bootcamp-bundle.png\" \n      style=\"width: 250px; height: 250px\"\n      title=\"3D Render of the Full Stack Bootcamp bundle as though it were a physical box (and not just a virtual box)\"\n  />\n-->\n</article>\n\n\n<article class=\"plan solo-plan\">\n    <div class=\"h3-wrapper\">\n        <h3>Course Bundle</h3>\n    </div>\n    <!--<p class=\"most-popular\"><strong>Limited Time: 65% Off</strong></p>-->\n\n  <img\n      src=\"/static/images/boximages/fullstack-bootcamp-bundle.png\" \n      style=\"width: 150px; height: 150px\"\n      title=\"3D Render of the Full Stack Bootcamp bundle as though it were a physical box (and not just a virtual box)\"\n  />\n        <p style=\"font-size: 12px\">Kickstart\n        Coding Virtual Academy<br />Lifetime Access to Video Program<p>\n    <p class=\"prices\">\n        <span class=\"crossed-price\">$299 MSRP</span>\n    </p>\n    <p class=\"prices\">\n        <span class=\"big-price\">92</span>\n        <!--<p>Coming soon: June 1st 2024</p>-->\n        <p>DISCOUNT CODE: <tt>EARLYBIRD24</tt></p>\n    </p>\n    <a class=\"buy-btn\" href=\"https://buy.stripe.com/eVa9CG7cZ6Plg0M3cc?prefilled_promo_code=EARLYBIRD24\">Buy Now</a>\n    <!--<a class=\"buy-btn\" href=\"#\">Coming soon</a>-->\n    <p style=\"font-size: 12px\">Purchase Secured By Stripe &reg;<p>\n</article>\n\n<article class=\"solo-info\">\n    <h2>Kickstart Coding Video Program</h2>\n    <img src=\"/static/images/characters_version_2_altered.png\" style=\"height: 300px; float: right;\" />\n    <ul style=\"text-align: left\">\n        <li>75+ hours of course videos</li>\n        <li>200+ activities &amp; mini-projects</li>\n        <li>50+ cheat-sheets &amp; study guides</li>\n        <li><strong>E-book included!</strong> - Kickstart Career is a 94\n        page guide-book to the tech job hunt, crammed with expert\n        tips from industry veterans\n        <li><strong>Easy Study Plan Creator</strong> - <em>New in 2024!</em> A\n        \"smart\" calendar assistant that assigns you due dates based\n        based on your goals and skill-level\n    </ul>\n</article>\n\n<article class=\"solo-info\" style=\"background: #eee\">\n    <h2>Tried-and-true curriculum</h2>\n    <p class=\"summary-solo\">For 6 years, our in-person and hybrid school has\n    guided 100s of students to build apps, unlock their creative potentials, or\n    find fulfilling professions in Silicon Valley, and beyond.</p>\n    </p>\n\n    <p class=\"summary-solo\">In 2024, we're proud to announce the\n    <strong>Kickstart Coding Virtual Coding Academy</strong>.\n    This means you can take our same tried-and-true bootcamp program, but\n    with the flexibility and (and affordability!) format yet!</p>\n    <br />\n    <p class=\"stars\">\n        &#9733;&#9733;&#9733;&#9733;&#9733;<br />
849\n        <span>Five-star rated school*</span>\n    </p>\n    <br />\n</article>\n\n</section>\n\n<div class=\"review-site-links\">\n    <p>* We are proud to maintain 5/5 for our commitment to\n    excellence in affordable coding education. Review sites:\n    <a href=\"https://www.coursereport.com/schools/kickstart-coding\" target=\"_blank\">Course Report</a>,\n    <a href=\"https://www.switchup.org/bootcamps/kickstart-coding\" target=\"_blank\">Switch Up</a>,\n    and <a href=\"https://www.yelp.com/biz/kickstart-coding-oakland\" target=\"_blank\">Yelp</a>.\n</div>");
850
851return OUT.join(""); };
852};
853window.moduloBuild.nameToHash.library1_PlanMenu_template_solo = "x18fqnr4";
854
855window.moduloBuild.modules["x16a3vqa"] = function library1_PlanMenu_template (modulo) {
856return function (CTX, G) { var OUT=[];
857  if (CTX.props.short) {
858  OUT.push("\n            ");
859  OUT.push(CTX.template_solo.render(CTX));
860  OUT.push("\n        ");
861  } else {
862  OUT.push("\n            ");
863  OUT.push(CTX.template_regular.render(CTX));
864  OUT.push("\n        ");
865  }
866
867return OUT.join(""); };
868};
869window.moduloBuild.nameToHash.library1_PlanMenu_template = "x16a3vqa";
870
871window.moduloBuild.modules["x1nl2kok"] = function library1_Courses_template (modulo) {
872return function (CTX, G) { var OUT=[];
873  if (!(CTX.props.topthree)) {
874  OUT.push("\n    <h1>Courses</h1>\n    <p>Our curriculum consists of 5 multi-segmented courses, which we use to\n    create a custom paced full stack curriculum for each student in our\n    program.</p>\n    <nav>\n        <ul>\n            ");
875  var ARR1=CTX.script.courses;for (var KEY in ARR1) {CTX. course=ARR1[KEY];
876  OUT.push("\n                <li>\n                    <button @click:=script.selectCourse payload=\"");
877  OUT.push(G.escapeText(G.filters["lower"](CTX.course.title)));
878  OUT.push("\">\n                        <span class=\"step\">");
879  OUT.push(G.escapeText(CTX.course.step));
880  OUT.push("</span>\n                        <span class=\"label\">");
881  OUT.push(G.escapeText(CTX.course.title));
882  OUT.push("</span>\n                        <span class=\"weeks\">");
883  OUT.push(G.escapeText(CTX.course.weeks));
884  OUT.push("</span>\n                    </button>\n                </li>\n            ");
885  }
886  OUT.push("\n        </ul>\n    </nav>\n\n    <h1>Course Segments</h1>\n");
887  }
888  OUT.push("\n\n<main>\n\n");
889  if (CTX.state.selected) {
890  OUT.push("\n    <aside>\n        ");
891  OUT.push(G.escapeText(G.filters["length"](CTX.script.visibleSegments)));
892  OUT.push("/");
893  OUT.push(G.escapeText(G.filters["length"](CTX.script.segments)));
894  OUT.push("\n        <button @click:=script.selectCourse payload=\"\">");
895  OUT.push(G.escapeText(CTX.state.selected));
896  OUT.push(" x</button>\n    </aside>\n");
897  }
898  OUT.push("\n\n\n");
899  var ARR0=CTX.script.visibleSegments;for (var KEY in ARR0) {CTX. seg=ARR0[KEY];
900  OUT.push("\n    <article title=\"");
901  OUT.push(G.escapeText(CTX.seg.subtitle));
902  OUT.push("\">\n        <div class=\"course-wrapper ");
903  if (CTX.state.selected) {
904  OUT.push("course-wrapper--selected");
905  }
906  OUT.push("\">\n            <div class=\"h3-wrapper\">\n                ");
907  if (CTX.seg.title) {
908  OUT.push("\n                    <h3>");
909  OUT.push(G.escapeText(CTX.seg.title));
910  OUT.push("</h3>\n                ");
911  } else {
912  OUT.push("\n                    <h3>");
913  OUT.push(G.escapeText(G.filters["capfirst"](CTX.seg.course)));
914  OUT.push("</h3>\n                ");
915  }
916  OUT.push("\n                <div style=\"\n                      background-image: \n                          ");
917  if (CTX.seg.imgfg === "white") {
918  OUT.push("\n                            linear-gradient(to bottom left, #11111155, #33333355),\n                          ");
919  } else {
920  OUT.push("\n                            linear-gradient(to bottom left, #eeeeee55, #cccccc55),\n                          ");
921  }
922  OUT.push("\n                          url(/static/images/new_course_photos/");
923  OUT.push(G.escapeText(G.filters["default"](CTX.seg.img,"laptop-g4d7844033_1280.png")));
924  OUT.push(");\n                      color: ");
925  OUT.push(G.escapeText(G.filters["default"](CTX.seg.imgfg,"black")));
926  OUT.push("\n                  \"\n                  class=\"courseimg\"\n                >\n                ");
927  if (CTX.seg.subtitle) {
928  OUT.push("\n                    <h4>");
929  OUT.push(G.escapeText(CTX.seg.subtitle));
930  OUT.push("</h4>\n                ");
931  }
932  OUT.push("\n                </div>\n            </div>\n            ");
933  if (CTX.seg.free) {
934  OUT.push("\n                <p class=\"free\" title=\"This course is free. Start immediately!\">FREE!</p>\n            ");
935  }
936  OUT.push("\n            ");
937  if (CTX.seg.content) {
938  OUT.push("\n                <p>");
939  OUT.push(G.escapeText(CTX.seg.content));
940  OUT.push("</p>\n            ");
941  }
942  OUT.push("\n            ");
943  if (CTX.seg.updated) {
944  OUT.push("\n                <p>");
945  OUT.push(G.escapeText(CTX.seg.updated));
946  OUT.push("</p>\n            ");
947  }
948  OUT.push("\n            <div class=\"bottom-wrapper\">\n            ");
949  if (CTX.seg.skill) {
950  OUT.push("\n                <p class=\"skill skill-level");
951  OUT.push(G.escapeText(CTX.seg.skill));
952  OUT.push("\">\n                    ");
953  OUT.push("\n                    ");
954  if (CTX.seg.skill === 1) {
955  OUT.push("  □ □ □    <br />Warm-Up\n                    ");
956  } else if (CTX.seg.skill === 2) {
957  OUT.push("▣ □ □    <br />Beginner\n                    ");
958  } else if (CTX.seg.skill === 3) {
959  OUT.push("▣ ▣ □    <br />Intermediate\n                    ");
960  } else if (CTX.seg.skill === 4) {
961  OUT.push("▣ ▣ ▣    <br />Advanced\n                    ");
962  }
963  OUT.push("\n                </p>\n            ");
964  }
965  OUT.push("\n            <div class=\"buy-button-wrapper\">\n                ");
966  if (CTX.seg.modules) {
967  OUT.push("\n                    <strong class=\"modules\" title=\"");
968  OUT.push(G.escapeText(G.filters["length"](CTX.seg.modules)));
969  OUT.push(" modules\">");
970  OUT.push(G.escapeText(G.filters["length"](CTX.seg.modules)));
971  OUT.push(" <i class=\"bi bi-boxes\" aria-hidden=\"true\"></i></strong>\n                    <br />\n                ");
972  }
973  OUT.push("\n                ");
974  if (CTX.seg.project) {
975  OUT.push("\n                    <strong class=\"project-tag\" title=\"Project based learning: ");
976  OUT.push(G.escapeText(CTX.seg.project));
977  OUT.push("\"><i class=\"bi bi-laptop\" aria-hidden=\"true\"></i> PROJECT</strong>\n                ");
978  }
979  OUT.push("\n                ");
980  if (!(CTX.seg.url)) {
981  OUT.push("\n                    ");
982  OUT.push("\n                    <div class=\"included-explainer\">\n                        ");
983  if (CTX.seg.project) {
984  OUT.push("\n                            <strong>Project based learning:</strong> ");
985  OUT.push(G.escapeText(CTX.seg.project));
986  OUT.push("<br />\n                        ");
987  }
988  OUT.push("\n                        ");
989  if (CTX.seg.modules) {
990  OUT.push("\n                            <strong>Module Count:</strong> ");
991  OUT.push(G.escapeText(G.filters["length"](CTX.seg.modules)));
992  OUT.push("\n                        ");
993  }
994  OUT.push("\n                    </div>\n                ");
995  }
996  OUT.push("\n            </div>\n\n            ");
997  if (CTX.seg.url) {
998  OUT.push("\n                <p class=\"url\">\n                    <a href=\"");
999  OUT.push(G.escapeText(CTX.seg.url));
1000  OUT.push("\" class=\"Page-navLink\" style=\"min-width: 40px; font-size: 1rem\">\n                        BEGIN&nbsp;&raquo;\n                    </a>\n                    ");
1001  if (CTX.props.topthree) {
1002  OUT.push("\n                        <span class=\"sticky-note\">\n                            <span class=\"sticky-note-arrow\">⬆</span>\n                            <strong>Free Course for Beginners!</strong>\n                        </span>\n                    ");
1003  }
1004  OUT.push("\n\n                </p>\n            ");
1005  } else if (CTX.props.topthree) {
1006  OUT.push("\n                ");
1007  OUT.push("\n            ");
1008  } else {
1009  OUT.push("\n                <div class=\"buy-button-wrapper\">\n                    <a href=\"/apply/\" class=\"getbtn\">\n                    GET&nbsp;<i class=\"fa fa-shopping-cart\" aria-hidden=\"true\"></i>\n                    </a>\n                    <div class=\"included-explainer\">\n                        <strong style=\"font-size: 1.5rem;\">$299 MSRP</strong>\n                        <em>LIFETIME DIGITAL ACCESS BUNDLE</em>\n                        <p>&raquo; Lifetime access to Kickstart Coding\n                        video courses (includes 100s of hours of private\n                        video archive access)\n                        <br />\n                        &raquo; Lifetime access to Kickstart Coding's\n                        coding curriculum files (includes 100s of\n                        professional assignments and industry-tuned example\n                        projects)\n                        <br />\n                        &raquo; Lifetime access to Virtual Coding\n                        Academy software (includes Linux Lab distro and\n                        other interactive courseware)</p>\n                        <!--\n                        <a href=\"https://kickstartcoding.online/apply
1009/\" class=\"Page-navLink\" style=\"min-width: 40px; font-size: 1rem\">\n                          🛒&nbsp;GET&nbsp;&raquo;\n                        </a>\n                        -->\n                    </div>\n                </div>\n            ");
1010  }
1011  OUT.push("\n            </div>\n        </div>\n    </article>\n");
1012  }
1013  OUT.push("\n\n</main>");
1014
1015return OUT.join(""); };
1016};
1017window.moduloBuild.nameToHash.library1_Courses_template = "x1nl2kok";
1018
1019window.moduloBuild.definitions = { 
1020
1021
1022
1023
1024
1025
1026modulo: {"Parent":null,"DefName":null,"Type":"Modulo","ChildPrefix":"","Contains":"coreDefs","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"defaultDef":{"DefTarget":null,"DefinedAs":null,"DefName":null},"defaultDefLoaders":["DefTarget","DefinedAs","Src"],"defaultFactory":["RenderObj","factoryCallback"],"src":"/static/js/Modulo.js","Name":"modulo","DefinitionName":"modulo","Source":"http://127.0.0.1:6627/static/libraries/","ChildrenNames":["configuration","configuration1","library","library1"]},
1027
1028configuration: {"Parent":"modulo","DefName":null,"Type":"Configuration","DefLoaders":["DefTarget","DefinedAs","Src|SrcSync","Content|Code","DefinitionName|MainRequire"],"Name":"configuration"},
1029
1030configuration1: {"Parent":"modulo","DefName":null,"Type":"Configuration","DefLoaders":["DefTarget","DefinedAs","Src|SrcSync","Content|Code","DefinitionName|MainRequire"],"Name":"configuration1","Source":"http://127.0.0.1:6627/static/js/ModuloInclude.js"},
1031
1032library: {"Parent":"modulo","DefName":null,"Type":"Library","Contains":"coreDefs","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"namespace":"x","Name":"library","DefinitionName":"library","Source":"http://127.0.0.1:6627/static/libraries/layout.html","ChildrenNames":["library_include","library_configuration","library_Page","library_FeaturedTestimonials","library_Testimonials","library_FeaturedArticles"]},
1033
1034library1: {"Parent":"modulo","DefName":null,"Type":"Library","Contains":"coreDefs","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"namespace":"x","Name":"library1","DefinitionName":"library1","Source":"http://127.0.0.1:6627/static/libraries/shop.html","ChildrenNames":["library1_configuration","library1_StudentIDGenerator","library1_HiddenSuccessCard","library1_PlanMenu","library1_Courses"]},
1035
1036library_include: {"Parent":"library","DefName":null,"Content":"\n    <link rel=\"stylesheet\" href=\"/static/css/site.css\">\n","GlobalBundled":{"xx7ae92e":true},"ServerTemplate":"https://{{ server }}/{{ path }}","TagTemplate":"{% if isCSS %}<link name=\"{{ package }}\" rel=\"stylesheet\" href=\"{{ url }}\" />{% else %}<script name=\"{{ package }}\" src=\"{{ url }}\"></script>{% endif %}","DefLoaders":["DefTarget","DefinedAs","Src","Server","LoadMode"],"Type":"Include","N
1036ame":"include","DefinitionName":"library_include"},
1037
1038library_configuration: {"Parent":"library","DefName":null,"Type":"Configuration","DefLoaders":["DefTarget","DefinedAs","Src|SrcSync","Content|Code","DefinitionName|MainRequire"],"Name":"configuration"},
1039
1040library_Page: {"Parent":"library","DefName":null,"tagAliases":{},"mode":"vanish-into-document","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"Page","Name":"Page","DefinitionName":"library_Page","ChildrenNames":["library_Page_props","library_Page_template","library_Page_style","library_Page_script"],"namespace":"x","TagName":"x-page","className":"x_Page"},
1041
1042library_FeaturedTestimonials: {"Parent":"library","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"FeaturedTestimonials","Name":"FeaturedTestimonials","DefinitionName":"library_FeaturedTestimonials","ChildrenNames":["library_FeaturedTestimonials_template","library_FeaturedTestimonials_style","library_FeaturedTestimonials_state","library_FeaturedTestimonials_staticdata"],"namespace":"x","TagName":"x-featuredtestimonials","className":"x_FeaturedTestimonials"},
1043
1044library_Testimonials: {"Parent":"library","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"Testimonials","Name":"Testimonials","DefinitionName":"library_Testimonials","ChildrenNames":["library_Testimonials_template","library_Testimonials_style","library_Testimonials_state","library_Testimonials_staticdata"],"namespace":"x","TagName":"x-testimonials","className":"x_Testimonials"},
1045
1046library_FeaturedArticles: {"Parent":"library","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"FeaturedArticles","Name":"FeaturedArticles","DefinitionName":"library_FeaturedArticles","ChildrenNames":["library_FeaturedArticles_template","library_FeaturedArticles_state","library_FeaturedArticles_staticdata","library_FeaturedArticles_script","library_FeaturedArticles_style"],"namespace":"x","TagName":"x-featuredarticles","className":"x_FeaturedArticles"},
1047
1048library_Page_props: {"Parent":"library_Page","DefName":null,"Content":"","Type":"Props","title":"","toptitle":"","rightimage":"","tlcutout":"","tlcutoutbg":"","tltall":"","bottomtitle":"","navselected":"","landing":"","Name":"props","DefinitionName":"library_Page_props"},
1049
1050library_Page_template: {"Parent":"library_Page","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{}
1050,"Name":"template","DefinitionName":"library_Page_template","Source":"http://127.0.0.1:6627/static/libraries/layout/Page.html"},
1051
1052library_Page_style: {"Parent":"library_Page","DefName":null,"urlReplace":null,"isolateSelector":null,"isolateClass":null,"prefix":null,"corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library_Page_style","Source":"http://127.0.0.1:6627/static/libraries/layout/Page.css","headHash":"x1jmv0rr"},
1053
1054library_Page_script: {"Parent":"library_Page","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library_Page_script","exportNames":["prepareCallback"],"locals":[],"Directives":[],"tempContent":"\n        function prepareCallback() {\n            // For now, store the navigation array and generate year in JS\n            return {\n                currentYear: (new Date()).getFullYear(),\n                navigation: [\n                    {\n                        title: \"Start\",\n                        file: \"index.html\",\n                    },\n                    {\n                        title: \"Courses\",\n                        file: \"courses/\",\n                    },\n                    {\n                        title: \"Plans\",\n                        //file: \"index.html#learn\",\n                        file: \"apply/\",\n                    },\n                    {\n                        title: \"Services\",\n                        file: \"services/\",\n                    },\n                    /*\n                    {\n                        title: \"Results\",\n                        file: \"results/\",\n                    },\n                    {\n                        title: \"Learn\",\n                        //file: \"index.html#learn\",\n                        file: \"learn/\",\n                    },\n                    {\n                        title: \"Outreach\",\n                        file: \"outreach/\",\n                    },\n                    */\n                ],\n            };\n        }\n    "},
1055
1056library_FeaturedTestimonials_template: {"Parent":"library_FeaturedTestimonials","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template","DefinitionName":"library_FeaturedTestimonials_template","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.html"},
1057
1058library_FeaturedTestimonials_style: {"Parent":"library_FeaturedTestimonials","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-FeaturedTestimonials","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library_FeaturedTestimonials_style","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.css","headHash":"xxp167be"},
1059
1060library_FeaturedTestimonials_state: {"Parent":"library_FeaturedTestimonials","DefName":null,"Content":"","Type":"State","Directives":["bindMount","bindUnmount"],"Store":null,"selected":"","Name":"state","DefinitionName":"library_FeaturedTestimonials_state"},
1061
1062library_FeaturedTestimonials_staticdata: {"Parent":"library_FeaturedTestimonials","DefName":null,"Content":"[\n    {\n        name: 'Nicolas Miller',\n        img: 'nicolas.jpg',\n        location: 'Antioch, California',\n        profession: 'Software Engineer @ Rooster Grin Media',\n        linkedin: 'nicolas-wyatt-miller',\n        pullquote: `The custom paced structure was possibly the most vital component to\n            my success [...] this setup allowed me to pace my progress and\n            uphold my life's balance in the process.`,\n        text: `\n            <strong>It sounds cheesy, but Kickstart Coding gave me the\n            opportunity to make a career shift that I never thought I would be\n            able to.<
1062/strong> The personalized tutoring helped target my\n            weaknesses and get honest feedback on my projects that large scale\n            programs are unable to provide. The custom paced structure was\n            possibly the most vital component to my success; I've never been\n            the person who gets something new on the first, second, or even\n            third try, but this setup allowed me to pace my progress and uphold\n            my life's balance in the process. The instructional material was\n            intense, but Michael makes some hilarious references and segments\n            the topics into digestible lessons. If I could give any advice, I\n            would say to not let anything stop you, but ESPECIALLY don't let\n            you stop yourself. You 1000% can do this and self doubt or fear of\n            difficulty are likely to be your biggest obstacles -- you just have\n            to be kind to yourself and get up again when JavaScript and CSS\n            inevitably knock you down. <strong>You are going to be so proud of\n            yourself because this is massive and this is worth it!</strong>\n        `,\n    },\n\n    {\n        name: 'Anthony Sifontes',\n        img: 'anthony.jpg',\n        location: 'New Orleans, Louisiana',\n        profession: 'Software Engineer @ Amino',\n        linkedin: 'anthonysifontes',\n        featured: 'Software Engineer',\n        pullquote: `I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.`,\n        text: `\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        `,\n    },\n\n    {\n        name: 'Andrew Thorton',\n        img: 'andrew.jpg',\n        location: 'Alameda, California',\n        profession: 'Data Engineer @ Lyft',\n        featured: 'Data Engineer',\n        linkedin: 'andrew-w-thornton',\n        pullquote: `Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.`,\n        text: `\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focu
1062sed on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        `,\n    },\n\n    {\n        name: 'Sarah Farnsworth-Kumli',\n        img: 'sarah.jpg',\n        location: 'San Francisco, California',\n        profession: 'Solutions Engineer @ Okta',\n        linkedin: 'sarah-fk',\n        pullquote: `I walked into Kickstart Coding to brush up on my skills. I\n        left with a new career.`,\n        text: `\n            <strong>Now that I am on the other side, have landed a job, and am\n            now working with developers I can honestly say that Kickstart is a\n            quality program.</strong> They take a modern approach to teaching\n            and teach what's current in an adapting industry (also what stacks\n            companies are hiring for!).\n\n            Michael covered CS concepts in an incredibly accessible way for a\n            diverse classroom with different levels of experience.  The course\n            material was the perfect balance of providing introductory guidance\n            and challenge. Group projects exposed us to the challenges of\n            working in a development team and we continued to build on new\n            iterations of our personal projects.\n\n            It's a privilege to have been a part of such a fantastic community,\n            and I'm excited to see it grow. <strong>Amazing how such a short\n            time can change your life, if you're considering taking the plunge,\n            DO IT!</strong>\n        `,\n    },\n\n    {\n        name: 'Nicol Hawkins',\n        featured: 'Platform Specialist',\n        img: 'nicol.jpg',\n        location: 'San Franciso, California',\n        profession: 'Platform Specialist @ Amplitude',\n        linkedin: 'nicol-hawkins',\n        featured_on_frontpage: true,\n        pullquote: `Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.`,\n        featured_text: `\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        `,\n        text: `\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        `,\n    },\n\n    {\n        name: 'Yulia Tuzov',\n        img: 'yulia.jpg',\n        location: 'San Mateo, California',\n        profession: 'QA engineer @ Skupos',\n        linkedin: 'yuliatuzov',\n        pullquote: `Kickstart’s curriculum gave me technical understanding of\n        all the moving parts involved in building a software product.`,\n        text: `\n            I have been in tech for couple of years in a QA role prior to going\n            to this bootcamp.  And now transitioning into an engineering role.\n            Kickstart’s curriculum gave me technical understanding of all the\n            moving parts involved in building a software product as well as\n            practice of building those parts in group projects and individual\n            projects which is extremely valuable experience.\n            \n            The curriculum overall is very well structured in a way that after\n            every topic is introduced students had to apply it in their\
1062n            homework right away as well as final projects at the end of each\n            course. Program also includes industry insights and job prep\n            material. <strong>Michael, Mark and Maddy are very passionate about\n            their students to succeed in the career transitions. You do have to\n            work a lot during the course, so be ready, but you’ll also have\n            amazing support.</strong> Support also doesn’t end after the\n            course, you can come to their hack & learns to get help or\n            guidance.\n        `,\n    },\n\n    {\n        name: 'Kyle Pollock',\n        img: 'kyle.jpg',\n        location: 'San Francisco, California',\n        profession: 'Knowledge Base Manager @ Synapse',\n        linkedin: 'kylepollock',\n        pullquote: `I got a job offer within 30 days of completing my 4 month summer cohort.`,\n        text: `\n            I was looking for a place to learn Python, JavaScript, and how to work\n            with APIs to increase my skills, and make myself more marketable as a\n            former jack of all trades website manager.  I was quite familiar with\n            the coding bootcamps in the Bay Area, as a friend of mine helped start\n            one in San Francisco. When I searched around for programs and found\n            Kickstart Coding, I couldn't find one that could beat it's overall\n            value through affordability, flexibility, and course offerings. I was\n            able to build three strong  projects in Github (along with other\n            projects) and an online portfolio. I got a job offer within 30 days of\n            completing my 4 month summer cohort. <strong>I also more than tripled\n            my income from my previous job as a non-profit communications manager.\n            I am grateful to have gone through Kickstart Coding, and for the\n            ongoing support beyond graduation through the teachers and alumni\n            group.</strong>\n        `,\n    },\n\n    {\n        name: 'Rudy Trubitt',\n        img: 'rudy.jpg',\n        location: 'Oakland, California',\n        profession: 'Senior Software Test Engineer @ Dolby',\n        linkedin: 'rudy-trubitt-43b2126',\n        pullquote: `Kickstart Coding gave me the key skills I needed to advance my career.`,\n        text: `\n            <strong>I needed a career boost and Kickstart delivered.</strong>\n            From the start, I was impressed by my interactions with the\n            program’s staff.  Although I was looking specifically for Python\n            instruction, the additional material in the back-end course turned\n            out to be extremely relevant. The class work was challenging but\n            well-differentiated for students with varying levels of programming\n            experience. And I thoroughly enjoyed getting to know and work with\n            everyone in my cohort.\n\n            During my job hunting and interviewing, I regularly reflected on\n            things I learned in the program. <strong>There were times when I\n            literally could hear Michael's voice in my head while I answered\n            interview questions.</strong> Although my previous skills also\n            helped me land my current position, there's no doubt that Kickstart\n            Coding gave me the key skills I needed to advance my career.\n        `,\n    },\n\n    {\n        name: 'Cindy Dovinh',\n        img: 'cindy.jpg',\n        location: 'Oakland, California',\n        profession: '',\n        pullquote: `Kickstart Coding delivered on commitment to value, education, and passion.`,\n        text: `\n            Kickstart Coding could not have been more aptly named and the\n            schedule/format of the class could not have fit my needs better.\n            With my full-time, non-tech job demanding a minimum of 50-60 hrs\n            per week, I still looked forward to every class (2 weekday evenings\n            and Saturday afternoon, each week).  Michael is hands down the best\n            teacher I've ever had. He is an expert at what he does, with an\n            impressively VAST knowledge base and still managed to consistently\n            make you feel comfortable in your learning. Being introduced to so\n            much new information and new ways of thinking, he was always able\n            to answer our questions thoroughly, even though at times, we didn't\n            even know how to ask them.\n        `,\n    },\n\n\n    {\n        name: 'Errin Mixon',\n        img: 'errin.jpg',\n        location: 'Oakland, California',\n        profession: 'Junior Frontend Developer @ GeoEx',\n        linkedin: 'errin-mixon-2b289023',\n        pullquote: `I couldn't have afforded the time or money to take a traditional\n            bootcamp. I came out the other end as a web developer – with a new job.`,\n        text: `\n            I come from a zero-tech background. I wanted to build an app. Michael\n            said he could teach me that, so I signed up.\n\n            I'm not going to lie. This bootcamp was hard. For a tech newbie with a\n            full-time job and family, the time commitment is intense. I was often\n            frustrated. I cried, a lot. I secretly thought that I shouldn't have\n            been admitted to the course.\n\n            But I came out the other end as a web developer – with a new job. That\n            hadn't even been my goal, and you couldn't find anybody more surprised\n            than me!\n\n            I couldn't have afforded the time or money to take a traditional\n            bootcamp. I never would have tried. <strong>Kickstart Coding made\n            it possible for me to learn these skills, and it's no exaggeration\n            to say that it changed the trajectory of my career, and possibly my\n            life.</strong> Michael is an excellent and engaging teacher, as are\n            TA's Maddy and Mark, and I owe them a debt of gratitude.\n\n            5 stars for Kickstart – well worth your money.\n        `,\n    },\n\n    {\n        name: 'Jeremy Johnson',\n        img: 'jeremy.jpg',\n        location: 'Madison, Wisconsin',\n        profession: 'Web Developer @ Know How',\n        linkedin: 'jeremy-johnson-80035395',\n        pullquote: `The skills I learned in this course I use every day at my new job\n            as a web developer.`,\n        text: `\n            Out of high school I got a 2 year web programming degree at a local\n            technical college. I applied and applied for programming jobs and\n            was unable to land one. I really enjoyed programming but started to\n            think I would never be able to make it my career. A few years went\n            by working odd jobs when I got the opportunity to take this\n            bootcamp online.\n            \n            <strong>I can't begin to say how great this course is. I learned\n            more about programming in a matter of weeks than I did in a matter\n            of years in school.</strong>  Seeing real world examples of what we\n            were learning along the way made me excited to learn and do the\n            work. In addition to the amazing coursework, Micheal equips you\n            with great tools to help you find a job. <strong>I was able to get\n            a programming job just weeks after completing the course.</strong>\n            The skills I learned in this course I use every day at my job as a\n            web developer.\n            \n            I am very happy with my new job and 100% recommend this course to\n            anyone looking to get into tech!\n        `,\n    },\n]\n\n        .filter(\n            item => item.featured // only show featured\n        )\n    ","Type":"StaticData","DefLoa
1062ders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata","DefinitionName":"library_FeaturedTestimonials_staticdata","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.js","data":[{"name":"Anthony Sifontes","img":"anthony.jpg","location":"New Orleans, Louisiana","profession":"Software Engineer @ Amino","linkedin":"anthonysifontes","featured":"Software Engineer","pullquote":"I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.","text":"\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        "},{"name":"Andrew Thorton","img":"andrew.jpg","location":"Alameda, California","profession":"Data Engineer @ Lyft","featured":"Data Engineer","linkedin":"andrew-w-thornton","pullquote":"Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.","text":"\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focused on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        "},{"name":"Nicol Hawkins","featured":"Platform Specialist","img":"nicol.jpg","location":"San Franciso, California","profession":"Platform Specialist @ Amplitude","linkedin":"nicol-hawkins","featured_on_frontpage":true,"pullquote":"Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.","featured_text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        ","text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        "}]},
1063
1064library_Testimonials_template: {"Parent":"library_Testimonials","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X >
1064 Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template","DefinitionName":"library_Testimonials_template","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.html"},
1065
1066library_Testimonials_style: {"Parent":"library_Testimonials","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-Testimonials","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library_Testimonials_style","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.css","headHash":"xx357jf0"},
1067
1068library_Testimonials_state: {"Parent":"library_Testimonials","DefName":null,"Content":"","Type":"State","Directives":["bindMount","bindUnmount"],"Store":null,"selected":"","Name":"state","DefinitionName":"library_Testimonials_state"},
1069
1070library_Testimonials_staticdata: {"Parent":"library_Testimonials","DefName":null,"Content":"[\n    {\n        name: 'Nicolas Miller',\n        img: 'nicolas.jpg',\n        location: 'Antioch, California',\n        profession: 'Software Engineer @ Rooster Grin Media',\n        linkedin: 'nicolas-wyatt-miller',\n        pullquote: `The custom paced structure was possibly the most vital component to\n            my success [...] this setup allowed me to pace my progress and\n            uphold my life's balance in the process.`,\n        text: `\n            <strong>It sounds cheesy, but Kickstart Coding gave me the\n            opportunity to make a career shift that I never thought I would be\n            able to.</strong> The personalized tutoring helped target my\n            weaknesses and get honest feedback on my projects that large scale\n            programs are unable to provide. The custom paced structure was\n            possibly the most vital component to my success; I've never been\n            the person who gets something new on the first, second, or even\n            third try, but this setup allowed me to pace my progress and uphold\n            my life's balance in the process. The instructional material was\n            intense, but Michael makes some hilarious references and segments\n            the topics into digestible lessons. If I could give any advice, I\n            would say to not let anything stop you, but ESPECIALLY don't let\n            you stop yourself. You 1000% can do this and self doubt or fear of\n            difficulty are likely to be your biggest obstacles -- you just have\n            to be kind to yourself and get up again when JavaScript and CSS\n            inevitably knock you down. <strong>You are going to be so proud of\n            yourself because this is massive and this is worth it!</strong>\n        `,\n    },\n\n    {\n        name: 'Anthony Sifontes',\n        img: 'anthony.jpg',\n        location: 'New Orleans, Louisiana',\n        profession: 'Software Engineer @ Amino',\n        linkedin: 'anthonysifontes',\n        featured: 'Software Engineer',\n        pullquote: `I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.`,\n        text: `\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        `,\n    },\n\n    {\n        name: 'Andrew Thorton',\n        img: 'andrew.jpg',\n        location: 'Alameda, California',\n        profession: 'Data 
1070Engineer @ Lyft',\n        featured: 'Data Engineer',\n        linkedin: 'andrew-w-thornton',\n        pullquote: `Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.`,\n        text: `\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focused on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        `,\n    },\n\n    {\n        name: 'Sarah Farnsworth-Kumli',\n        img: 'sarah.jpg',\n        location: 'San Francisco, California',\n        profession: 'Solutions Engineer @ Okta',\n        linkedin: 'sarah-fk',\n        pullquote: `I walked into Kickstart Coding to brush up on my skills. I\n        left with a new career.`,\n        text: `\n            <strong>Now that I am on the other side, have landed a job, and am\n            now working with developers I can honestly say that Kickstart is a\n            quality program.</strong> They take a modern approach to teaching\n            and teach what's current in an adapting industry (also what stacks\n            companies are hiring for!).\n\n            Michael covered CS concepts in an incredibly accessible way for a\n            diverse classroom with different levels of experience.  The course\n            material was the perfect balance of providing introductory guidance\n            and challenge. Group projects exposed us to the challenges of\n            working in a development team and we continued to build on new\n            iterations of our personal projects.\n\n            It's a privilege to have been a part of such a fantastic community,\n            and I'm excited to see it grow. <strong>Amazing how such a short\n            time can change your life, if you're considering taking the plunge,\n            DO IT!</strong>\n        `,\n    },\n\n    {\n        name: 'Nicol Hawkins',\n        featured: 'Platform Specialist',\n        img: 'nicol.jpg',\n        location: 'San Franciso, California',\n        profession: 'Platform Specialist @ Amplitude',\n        linkedin: 'nicol-hawkins',\n        featured_on_frontpage: true,\n        pullquote: `Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.`,\n        featured_text: `\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        `,\n        text: `\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        `,\n    },\n\n    {\n        name: 'Yulia Tuzov',\n        img: 'yulia.jpg',\n        location: 'San Mateo, California',\n        profession: 'QA engineer @ Skupos',\n        linkedin: 'yuliatuzov',\n        pullquote: `Kickstart’s curriculum gave me technical understanding of\n        all the moving parts involved in building a software product.`,\n        text: `\n            I have been in tech for couple of years in a QA role prior to going\n            to this bootcamp.  And now transitioning into an engineering role.\n            Kickstart’s curriculum gave me technical understanding of all the\n            moving parts involved in building a software product as well as\n            practice of building those parts in group projects and individual\n            projects which is extremely valuable experience.\n            \n            The curriculum overall is very well structured in a way that after\n            every topic is introduced students had to apply it in their\
1070n            homework right away as well as final projects at the end of each\n            course. Program also includes industry insights and job prep\n            material. <strong>Michael, Mark and Maddy are very passionate about\n            their students to succeed in the career transitions. You do have to\n            work a lot during the course, so be ready, but you’ll also have\n            amazing support.</strong> Support also doesn’t end after the\n            course, you can come to their hack & learns to get help or\n            guidance.\n        `,\n    },\n\n    {\n        name: 'Kyle Pollock',\n        img: 'kyle.jpg',\n        location: 'San Francisco, California',\n        profession: 'Knowledge Base Manager @ Synapse',\n        linkedin: 'kylepollock',\n        pullquote: `I got a job offer within 30 days of completing my 4 month summer cohort.`,\n        text: `\n            I was looking for a place to learn Python, JavaScript, and how to work\n            with APIs to increase my skills, and make myself more marketable as a\n            former jack of all trades website manager.  I was quite familiar with\n            the coding bootcamps in the Bay Area, as a friend of mine helped start\n            one in San Francisco. When I searched around for programs and found\n            Kickstart Coding, I couldn't find one that could beat it's overall\n            value through affordability, flexibility, and course offerings. I was\n            able to build three strong  projects in Github (along with other\n            projects) and an online portfolio. I got a job offer within 30 days of\n            completing my 4 month summer cohort. <strong>I also more than tripled\n            my income from my previous job as a non-profit communications manager.\n            I am grateful to have gone through Kickstart Coding, and for the\n            ongoing support beyond graduation through the teachers and alumni\n            group.</strong>\n        `,\n    },\n\n    {\n        name: 'Rudy Trubitt',\n        img: 'rudy.jpg',\n        location: 'Oakland, California',\n        profession: 'Senior Software Test Engineer @ Dolby',\n        linkedin: 'rudy-trubitt-43b2126',\n        pullquote: `Kickstart Coding gave me the key skills I needed to advance my career.`,\n        text: `\n            <strong>I needed a career boost and Kickstart delivered.</strong>\n            From the start, I was impressed by my interactions with the\n            program’s staff.  Although I was looking specifically for Python\n            instruction, the additional material in the back-end course turned\n            out to be extremely relevant. The class work was challenging but\n            well-differentiated for students with varying levels of programming\n            experience. And I thoroughly enjoyed getting to know and work with\n            everyone in my cohort.\n\n            During my job hunting and interviewing, I regularly reflected on\n            things I learned in the program. <strong>There were times when I\n            literally could hear Michael's voice in my head while I answered\n            interview questions.</strong> Although my previous skills also\n            helped me land my current position, there's no doubt that Kickstart\n            Coding gave me the key skills I needed to advance my career.\n        `,\n    },\n\n    {\n        name: 'Cindy Dovinh',\n        img: 'cindy.jpg',\n        location: 'Oakland, California',\n        profession: '',\n        pullquote: `Kickstart Coding delivered on commitment to value, education, and passion.`,\n        text: `\n            Kickstart Coding could not have been more aptly named and the\n            schedule/format of the class could not have fit my needs better.\n            With my full-time, non-tech job demanding a minimum of 50-60 hrs\n            per week, I still looked forward to every class (2 weekday evenings\n            and Saturday afternoon, each week).  Michael is hands down the best\n            teacher I've ever had. He is an expert at what he does, with an\n            impressively VAST knowledge base and still managed to consistently\n            make you feel comfortable in your learning. Being introduced to so\n            much new information and new ways of thinking, he was always able\n            to answer our questions thoroughly, even though at times, we didn't\n            even know how to ask them.\n        `,\n    },\n\n\n    {\n        name: 'Errin Mixon',\n        img: 'errin.jpg',\n        location: 'Oakland, California',\n        profession: 'Junior Frontend Developer @ GeoEx',\n        linkedin: 'errin-mixon-2b289023',\n        pullquote: `I couldn't have afforded the time or money to take a traditional\n            bootcamp. I came out the other end as a web developer – with a new job.`,\n        text: `\n            I come from a zero-tech background. I wanted to build an app. Michael\n            said he could teach me that, so I signed up.\n\n            I'm not going to lie. This bootcamp was hard. For a tech newbie with a\n            full-time job and family, the time commitment is intense. I was often\n            frustrated. I cried, a lot. I secretly thought that I shouldn't have\n            been admitted to the course.\n\n            But I came out the other end as a web developer – with a new job. That\n            hadn't even been my goal, and you couldn't find anybody more surprised\n            than me!\n\n            I couldn't have afforded the time or money to take a traditional\n            bootcamp. I never would have tried. <strong>Kickstart Coding made\n            it possible for me to learn these skills, and it's no exaggeration\n            to say that it changed the trajectory of my career, and possibly my\n            life.</strong> Michael is an excellent and engaging teacher, as are\n            TA's Maddy and Mark, and I owe them a debt of gratitude.\n\n            5 stars for Kickstart – well worth your money.\n        `,\n    },\n\n    {\n        name: 'Jeremy Johnson',\n        img: 'jeremy.jpg',\n        location: 'Madison, Wisconsin',\n        profession: 'Web Developer @ Know How',\n        linkedin: 'jeremy-johnson-80035395',\n        pullquote: `The skills I learned in this course I use every day at my new job\n            as a web developer.`,\n        text: `\n            Out of high school I got a 2 year web programming degree at a local\n            technical college. I applied and applied for programming jobs and\n            was unable to land one. I really enjoyed programming but started to\n            think I would never be able to make it my career. A few years went\n            by working odd jobs when I got the opportunity to take this\n            bootcamp online.\n            \n            <strong>I can't begin to say how great this course is. I learned\n            more about programming in a matter of weeks than I did in a matter\n            of years in school.</strong>  Seeing real world examples of what we\n            were learning along the way made me excited to learn and do the\n            work. In addition to the amazing coursework, Micheal equips you\n            with great tools to help you find a job. <strong>I was able to get\n            a programming job just weeks after completing the course.</strong>\n            The skills I learned in this course I use every day at my job as a\n            web developer.\n            \n            I am very happy with my new job and 100% recommend this course to\n            anyone looking to get into tech!\n        `,\n    },\n]\n\n        .map(\n            item => ({\n                ...item,\n                featured: null, // Blank out \"featured\" field for all Testimonials\n            }
1070)\n        )\n    ","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata","DefinitionName":"library_Testimonials_staticdata","Source":"http://127.0.0.1:6627/static/libraries/layout/Testimonials.js","data":[{"name":"Nicolas Miller","img":"nicolas.jpg","location":"Antioch, California","profession":"Software Engineer @ Rooster Grin Media","linkedin":"nicolas-wyatt-miller","pullquote":"The custom paced structure was possibly the most vital component to\n            my success [...] this setup allowed me to pace my progress and\n            uphold my life's balance in the process.","text":"\n            <strong>It sounds cheesy, but Kickstart Coding gave me the\n            opportunity to make a career shift that I never thought I would be\n            able to.</strong> The personalized tutoring helped target my\n            weaknesses and get honest feedback on my projects that large scale\n            programs are unable to provide. The custom paced structure was\n            possibly the most vital component to my success; I've never been\n            the person who gets something new on the first, second, or even\n            third try, but this setup allowed me to pace my progress and uphold\n            my life's balance in the process. The instructional material was\n            intense, but Michael makes some hilarious references and segments\n            the topics into digestible lessons. If I could give any advice, I\n            would say to not let anything stop you, but ESPECIALLY don't let\n            you stop yourself. You 1000% can do this and self doubt or fear of\n            difficulty are likely to be your biggest obstacles -- you just have\n            to be kind to yourself and get up again when JavaScript and CSS\n            inevitably knock you down. <strong>You are going to be so proud of\n            yourself because this is massive and this is worth it!</strong>\n        ","featured":null},{"name":"Anthony Sifontes","img":"anthony.jpg","location":"New Orleans, Louisiana","profession":"Software Engineer @ Amino","linkedin":"anthonysifontes","featured":null,"pullquote":"I eventually received an offer to be a Software Engineer at an exciting\n            healthcare start-up, and only 11 weeks into the program.","text":"\n            My career thus far had been in data analytics. I wanted to\n            transition to a developer role and ultimately be able to create and\n            turn my ideas into reality. I had not taken any coding courses\n            before this, only online tutorials and books, so I was not sure\n            what to expect.  <strong>Class after class, I was blown away by the\n            passion Michael and his team brought to each and every\n            lesson!</strong> The classes were lively, balanced between lecture,\n            demonstrations, guided coding activities, and group work, while\n            still being accessible to a wide range of experience levels.\n\n            A couple of months into the course, I started applying for\n            developer positions, and soon I was excited to be invited for a\n            round of on-site interviews. <strong>Michael was very supportive,\n            coaching me on how to best present my work experience, and guiding\n            me through a mock \"whiteboard\" interview.</strong> Every interview\n            was more challenging than the last, but all of the coaching and\n            prep received at Kickstart beforehand was critical to my moving\n            forward in the process. You can imagine my excitement when I\n            eventually received an offer to be a Software Engineer at an\n            exciting healthcare start-up, and only 11 weeks into the program!\n            Now, I've been at my new position for several months, and I love\n            it.  The work we did in class was instrumental in preparing me for\n            my new day-to-day duties.  I'm very thankful to the Kickstart\n            Coding team and the help they provided in transitioning to my new\n            software engineering career.\n        "},{"name":"Andrew Thorton","img":"andrew.jpg","location":"Alameda, California","profession":"Data Engineer @ Lyft","featured":null,"linkedin":"andrew-w-thornton","pullquote":"Using my newly acquired skills, I got a great new job as a Data\n            Engineer just a couple of weeks after finishing the Kickstart Coding\n            course.","text":"\n            After spending years as a software project and product manager flirting\n            with the idea of becoming a software engineer, I finally got up the\n            courage to sign up for a coding bootcamp. I looked at many different\n            options and chose Kickstart Coding because it provides excellent value\n            and is staffed by people that are clearly passionate about teaching\n            technology. I enjoyed the in-class lessons which are an engaging mix of\n            lecture, live-coding walkthroughs, and activities. The homework\n            assignments and group projects built nicely on the stuff we learned in\n            class and helped to reinforce the concepts with very practical\n            examples. <strong>The curriculum does a masterful job of challenging\n            people with some coding experience while also being accessible to\n            people with no coding background at all.</strong> While the vast\n            majority of the curriculum is very focu
1070sed on teaching specific\n            languages and frameworks, the last couple lessons of the Front-End\n            portion of the course are focused on some more general concepts that\n            were extremely helpful in preparing me for interviews. Using my newly\n            acquired skills, I got a great new job as a Data Engineer just a couple\n            of weeks after finishing the Kickstart Coding course. <strong>I would\n            recommend Kickstart Coding to everyone, even if you don't want a job as\n            a software engineer. Learning to code in this setting was really\n            fun!</strong>\n        "},{"name":"Sarah Farnsworth-Kumli","img":"sarah.jpg","location":"San Francisco, California","profession":"Solutions Engineer @ Okta","linkedin":"sarah-fk","pullquote":"I walked into Kickstart Coding to brush up on my skills. I\n        left with a new career.","text":"\n            <strong>Now that I am on the other side, have landed a job, and am\n            now working with developers I can honestly say that Kickstart is a\n            quality program.</strong> They take a modern approach to teaching\n            and teach what's current in an adapting industry (also what stacks\n            companies are hiring for!).\n\n            Michael covered CS concepts in an incredibly accessible way for a\n            diverse classroom with different levels of experience.  The course\n            material was the perfect balance of providing introductory guidance\n            and challenge. Group projects exposed us to the challenges of\n            working in a development team and we continued to build on new\n            iterations of our personal projects.\n\n            It's a privilege to have been a part of such a fantastic community,\n            and I'm excited to see it grow. <strong>Amazing how such a short\n            time can change your life, if you're considering taking the plunge,\n            DO IT!</strong>\n        ","featured":null},{"name":"Nicol Hawkins","featured":null,"img":"nicol.jpg","location":"San Franciso, California","profession":"Platform Specialist @ Amplitude","linkedin":"nicol-hawkins","featured_on_frontpage":true,"pullquote":"Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.","featured_text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  <strong>Kickstart Coding is more affordable\n            than most coding bootcamps and the curriculum is top-notch while having\n            a lot of repeat value.</strong> [...] I am blown away by how much I\n            learned since my first day at Kickstart Coding; from my first HTML file\n            all the way to navigating complex frameworks and implementing APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning.\n        ","text":"\n            When I started Kickstart Coding, I had very few programming skills and\n            no prior work experience in tech. It was a huge leap for me and it\n            literally changed my life!  Kickstart Coding is more affordable than\n            most coding bootcamps and the curriculum is top-notch while having a\n            lot of repeat value.\n\n            I left the course with a personal webpage full of projects to showcase\n            and revisited the curriculum often to tackle bonus activities. The\n            course is approachable whether you're like me and just starting out or\n            already possess programming skills. I am blown away by how much\n            I learned since my first day at Kickstart Coding; from my first HTML\n            file all the way to navigating complex frameworks and implementing\n            APIs.\n\n            This program not only gave me the technical skills required for a\n            career in tech -- it helped me recognize the potential and capacity I\n            have for learning. A major part of my new role as a Platform Specialist\n            is communicating with engineers and data analysts, so having a\n            background in software helps me communicate effectively and\n            efficiently. Thanks to the skills I learned at Kickstart Coding, I was\n            able to market myself for entry-level positions in the tech industry.\n        "},{"name":"Yulia Tuzov","img":"yulia.jpg","location":"San Mateo, California","profession":"QA engineer @ Skupos","linkedin":"yuliatuzov","pullquote":"Kickstart’s curriculum gave me technical understanding of\n        all the moving parts involved in building a software product.","text":"\n            I have been in tech for couple of years in a QA role prior to going\n            to this bootcamp.  And now transitioning into an engineering role.\n            Kickstart’s curriculum gave me technical understanding of all the\n            moving parts involved in building a software product as well as\n            practice of building those parts in group projects and individual\n            projects which is extremely valuable experience.\n            \n            The curriculum overall is very well structured in a way that after\n            every topic is introduced students had to apply it in their\
1070n            homework right away as well as final projects at the end of each\n            course. Program also includes industry insights and job prep\n            material. <strong>Michael, Mark and Maddy are very passionate about\n            their students to succeed in the career transitions. You do have to\n            work a lot during the course, so be ready, but you’ll also have\n            amazing support.</strong> Support also doesn’t end after the\n            course, you can come to their hack & learns to get help or\n            guidance.\n        ","featured":null},{"name":"Kyle Pollock","img":"kyle.jpg","location":"San Francisco, California","profession":"Knowledge Base Manager @ Synapse","linkedin":"kylepollock","pullquote":"I got a job offer within 30 days of completing my 4 month summer cohort.","text":"\n            I was looking for a place to learn Python, JavaScript, and how to work\n            with APIs to increase my skills, and make myself more marketable as a\n            former jack of all trades website manager.  I was quite familiar with\n            the coding bootcamps in the Bay Area, as a friend of mine helped start\n            one in San Francisco. When I searched around for programs and found\n            Kickstart Coding, I couldn't find one that could beat it's overall\n            value through affordability, flexibility, and course offerings. I was\n            able to build three strong  projects in Github (along with other\n            projects) and an online portfolio. I got a job offer within 30 days of\n            completing my 4 month summer cohort. <strong>I also more than tripled\n            my income from my previous job as a non-profit communications manager.\n            I am grateful to have gone through Kickstart Coding, and for the\n            ongoing support beyond graduation through the teachers and alumni\n            group.</strong>\n        ","featured":null},{"name":"Rudy Trubitt","img":"rudy.jpg","location":"Oakland, California","profession":"Senior Software Test Engineer @ Dolby","linkedin":"rudy-trubitt-43b2126","pullquote":"Kickstart Coding gave me the key skills I needed to advance my career.","text":"\n            <strong>I needed a career boost and Kickstart delivered.</strong>\n            From the start, I was impressed by my interactions with the\n            program’s staff.  Although I was looking specifically for Python\n            instruction, the additional material in the back-end course turned\n            out to be extremely relevant. The class work was challenging but\n            well-differentiated for students with varying levels of programming\n            experience. And I thoroughly enjoyed getting to know and work with\n            everyone in my cohort.\n\n            During my job hunting and interviewing, I regularly reflected on\n            things I learned in the program. <strong>There were times when I\n            literally could hear Michael's voice in my head while I answered\n            interview questions.</strong> Although my previous skills also\n            helped me land my current position, there's no doubt that Kickstart\n            Coding gave me the key skills I needed to advance my career.\n        ","featured":null},{"name":"Cindy Dovinh","img":"cindy.jpg","location":"Oakland, California","profession":"","pullquote":"Kickstart Coding delivered on commitment to value, education, and passion.","text":"\n            Kickstart Coding could not have been more aptly named and the\n            schedule/format of the class could not have fit my needs better.\n            With my full-time, non-tech job demanding a minimum of 50-60 hrs\n            per week, I still looked forward to every class (2 weekday evenings\n            and Saturday afternoon, each week).  Michael is hands down the best\n            teacher I've ever had. He is an expert at what he does, with an\n            impressively VAST knowledge base and still managed to consistently\n            make you feel comfortable in your learning. Being introduced to so\n            much new information and new ways of thinking, he was always able\n            to answer our questions thoroughly, even though at times, we didn't\n            even know how to ask them.\n        ","featured":null},{"name":"Errin Mixon","img":"errin.jpg","location":"Oakland, California","profession":"Junior Frontend Developer @ GeoEx","linkedin":"errin-mixon-2b289023","pullquote":"I couldn't have afforded the time or money to take a traditional\n            bootcamp. I came out the other end as a web developer – with a new job.","text":"\n            I come from a zero-tech background. I wanted to build an app. Michael\n            said he could teach me that, so I signed up.\n\n            I'm not going to lie. This bootcamp was hard. For a tech newbie with a\n            full-time job and family, the time commitment is intense. I was often\n            frustrated. I cried, a lot. I secretly thought that I shouldn't have\n            been admitted to the course.\n\n            But I came out the other end as a web developer – with a new job. That\n            hadn't even been my goal, and you couldn't find anybody more surprised\n            than me!\n\n            I couldn't have afforded the time or money to take a traditional\n            bootcamp. I never would have tried. <strong>Kickstart Coding made\n            it possible for me to learn these skills, and it's no exaggeration\n            to say that it changed the trajectory of my career, and possibly my\n            life.</strong> Michael is an excellent and engaging teacher, as are\n            TA's Maddy and Mark, and I owe them a debt of gratitude.\n\n            5 stars for Kickstart – well worth your money.\n        ","featured":null},{"name":"Jeremy Johnson","img":"jeremy.jpg","location":"Madison, Wisconsin","profession":"Web Developer @ Know How","linkedin":"jeremy-johnson-80035395","pullquote":"The skills I learned in this course I use every day at my new job\n            as a web developer.","text":"\n            Out of high school I got a 2 year web programming degree at a local\n            technical college. I applied and applied for programming jobs and\n            was unable to land one. I really enjoyed programming but started to\n            think I would never be able to make it my career. A few years went\n            by working odd jobs when I got the opportunity to take this\n            bootcamp online.\n            \n            <strong>I can't begin to say how great this course is. I learned\n            more about programming in a matter of weeks than I did in a matter\n            of years in school.</strong>  Seeing real world examples of what we\n            were learning along the way made me excited to learn and do the\n            work. In addition to the amazing coursework, Micheal equips you\n            with great tools to help you find a job. <strong>I was able to get\n            a programming job just weeks after completing the course.</strong>\n            The skills I learned in this course I use every day at my job as a\n            web developer.\n            \n            I am very happy with my new job and 100% recommend this course to\n            anyone looking to get into tech!\n        ","featured":null}]},
1071
1072library_FeaturedArticles_template: {"Parent":"library_FeaturedArticles","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X >
1072 Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template","DefinitionName":"library_FeaturedArticles_template","Source":"http://127.0.0.1:6627/static/libraries/layout/FeaturedArticles.html"},
1073
1074library_FeaturedArticles_state: {"Parent":"library_FeaturedArticles","DefName":null,"Content":"","Type":"State","Directives":["bindMount","bindUnmount"],"Store":null,"articles":[],"Name":"state","DefinitionName":"library_FeaturedArticles_state"},
1075
1076library_FeaturedArticles_staticdata: {"Parent":"library_FeaturedArticles","DefName":null,"Content":"\n        {\"results\": [{\"url\":\n        \"https://kickstartcoding.com/learn/pro-tips/six-important-tips-learning-code/\",\n        \"title\": \"Six Important Tips for Learning to Code\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/55/01/55011557-4ecf-4ce1-8efe-81555d5180d4/neat-lg-30ca40811ecee7f931167680e6b0412b.jpg\"},\n        {\"url\":\n        \"https://kickstartcoding.com/learn/articles/building-debug-trainer-deskop-app-tauri/\",\n        \"title\": \"Building the Debug Trainer Deskop App with Tauri\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/44/f4/44f43d18-76fc-47d2-aae7-0756d395fda4/tauri_homepage.png\"},\n        {\"url\": \"https://kickstartcoding.com/learn/articles/bootcamp-education-worth-it/\",\n        \"title\": \"Is a Bootcamp Education Worth the Cost?\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/cc/45/cc45281d-bef1-4ccc-ab0d-1da8fd1368e8/book-731199_1920.jpg\"}\n        ]}\n\n    ","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata","DefinitionName":"library_FeaturedArticles_staticdata","data":{"results":[{"url":"https://kickstartcoding.com/learn/pro-tips/six-important-tips-learning-code/","title":"Six Important Tips for Learning to Code","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/55/01/55011557-4ecf-4ce1-8efe-81555d5180d4/neat-lg-30ca40811ecee7f931167680e6b0412b.jpg"},{"url":"https://kickstartcoding.com/learn/articles/building-debug-trainer-deskop-app-tauri/","title":"Building the Debug Trainer Deskop App with Tauri","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/44/f4/44f43d18-76fc-47d2-aae7-0756d395fda4/tauri_homepage.png"},{"url":"https://kickstartcoding.com/learn/articles/bootcamp-education-worth-it/","title":"Is a Bootcamp Education Worth the Cost?","image":"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/cc/45/cc45281d-bef1-4ccc-ab0d-1da8fd1368e8/book-731199_1920.jpg"}]}},
1077
1078library_FeaturedArticles_script: {"Parent":"library_FeaturedArticles","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library_FeaturedArticles_script","exportNames":["initializedCallback"],"locals":["state"],"Directives":[],"tempContent":"\n        const hardcodedData = {\"results\": [{\"url\":\n        \"https://kickstartcoding.com/learn/pro-tips/six-important-tips-learning-code/\",\n        \"title\": \"Six Important Tips for Learning to Code\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/55/01/55011557-4ecf-4ce1-8efe-81555d5180d4/neat-lg-30ca40811ecee7f931167680e6b0412b.jpg?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=I8gFEnKJmZfY1bFG81cyiMJwKwY%3D&Expires=1678548551\"},\n        {\"url\":\n        \"https://kickstartcoding.com/learn/articles/building-debug-trainer-deskop-app-tauri/\",\n        \"title\": \"Building the Debug Trainer Deskop App with Tauri\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/44/f4/44f43d18-76fc-47d2-aae7-0756d395fda4/tauri_homepage.png?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=598nX2P36FOUspHce9oA9uSKzeE%3D&Expires=1678548551\"},\n        {\"url\":\n        \"https://kickstartcoding.com/learn/articles/bootcamp-education-worth-it/\",\n        \"title\": \"Is a Bootcamp Education Worth the Cost?\", \"image\":\n        \"https://sfo2.digitaloceanspaces.com/livesyllabus/ls1/filer_public/cc/45/cc45281d-bef1-4ccc-ab0d-1da8fd1368e8/book-731199_1920.jpg?AWSAccessKeyId=SQ2H24YZ2CVAWZIUNIC2&Signature=ce%2BC4bBeUveSy%2F5Z3Ix%2F4UrVLdY%3D&Expires=1678548551\"}]}\n        function initializedCallback() {\n            state.articles = hardcodedData.results;\n            /*\n            fetch(\"https://kickstartcoding.online/learn/api/featured/\")\n              .then(response => response.json())\n              .then(data => {\n                    console.log('featured data');\n              });\n            */\n        }\n    "},
1079
1080library_FeaturedArticles_style: {"Parent":"library_FeaturedArticles","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-FeaturedArticles","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library_FeaturedArticles_style","Source":"http://127.0.0.1:6627/static/libraries/layout/FeaturedArticles.css","headHash":"xxqdd4si"},
1081
1082library1_configuration: {"Parent":"library1","DefName":null,"Type":"Configuration","DefLoa
1082ders":["DefTarget","DefinedAs","Src|SrcSync","Content|Code","DefinitionName|MainRequire"],"Name":"configuration"},
1083
1084library1_StudentIDGenerator: {"Parent":"library1","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"StudentIDGenerator","Name":"StudentIDGenerator","DefinitionName":"library1_StudentIDGenerator","ChildrenNames":["library1_StudentIDGenerator_template","library1_StudentIDGenerator_style","library1_StudentIDGenerator_state","library1_StudentIDGenerator_script"],"namespace":"x","TagName":"x-studentidgenerator","className":"x_StudentIDGenerator"},
1085
1086library1_HiddenSuccessCard: {"Parent":"library1","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"HiddenSuccessCard","Name":"HiddenSuccessCard","DefinitionName":"library1_HiddenSuccessCard","ChildrenNames":["library1_HiddenSuccessCard_script"],"namespace":"x","TagName":"x-hiddensuccesscard","className":"x_HiddenSuccessCard"},
1087
1088library1_PlanMenu: {"Parent":"library1","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"PlanMenu","Name":"PlanMenu","DefinitionName":"library1_PlanMenu","ChildrenNames":["library1_PlanMenu_props","library1_PlanMenu_template_regular","library1_PlanMenu_template_solo","library1_PlanMenu_template","library1_PlanMenu_style","library1_PlanMenu_staticdata_full_plans","library1_PlanMenu_staticdata_short_list","library1_PlanMenu_script"],"namespace":"x","TagName":"x-planmenu","className":"x_PlanMenu"},
1089
1090library1_Courses: {"Parent":"library1","DefName":null,"tagAliases":{},"mode":"regular","rerender":"event","engine":"Reconciler","Contains":"cparts","RenderObj":"component","DefLoaders":["DefTarget","DefinedAs","Src","Content"],"DefBuilders":["CustomElement","alias|AliasNamespace","CodeTemplate"],"DefFinalizers":["MainRequire"],"BuildCommandBuilders":["Prebuild|BuildLifecycle","BuildLifecycle"],"Directives":["eventMount","eventUnmount","dataPropMount","dataPropUnmount"],"Type":"Component","name":"Courses","Name":"Courses","DefinitionName":"library1_Courses","ChildrenNames":["library1_Courses_props","library1_Courses_template","library1_Courses_style","library1_Courses_staticdata","library1_Courses_videostats","library1_Courses_state","library1_Courses_script"],"namespace":"x","TagName":"x-courses","className":"x_Courses"},
1091
1092library1_StudentIDGenerator_template: {"Parent":"library1_StudentIDGenerator","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template","DefinitionName":"library1_StudentIDGenerator_template","Source":"http://127.0.0.1:6627/static/libraries/shop/StudentIDGenerator.html"},
1093
1094library1_StudentIDGenerator_style: {"Parent":"library1_StudentIDGenerator","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-StudentIDGenerator","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library1_StudentIDGenerator_style","Source":"http://127.0.0.1:6627/static/libraries/shop/StudentIDGenerator.css","headHash":"x11tbipr"},
1095
1096library1_StudentIDGenerator_state: {"Parent":"library1_StudentIDGenerator","DefName":null,"Content":"","Type":"State","Directives":["bindMount","bindUnmount"],"Store":null,"first":"","last":"","email":"","submitted":false,"ready":null,"Name":"state","DefinitionName":"library1_StudentIDGenerator_state"},
1097
1098library1_StudentIDGenerator_script: {"Parent":"library1_StudentIDGenerator","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library1_StudentIDGenerator_script","Source":"http://127.0.0.1:6627/static/libraries/shop/StudentIDGenerator.js","exportNames":["prepareCallback","submit"],"locals":["template","state","cparts"],"Directives":[],"tempContent":"\nlet idcardTemplate = '';\nlet info = { };\n\nfunction prepareCallback() {\n    const params = Object.fromEntries(new URLSearchParams(window.document.location.search));\n    if (state.ready === null) {\n        state.ready = false;\n        // Can use \"idcard\" as a way to override the stripe URL to point to new\n        // ID card templates, as needed\n        //const htmlURL = '/static/idcards/' + (params.idcard || params.str
1098ipecode) + '.html';\n        const htmlURL = '/static/idcards/' + (params.idtemplate || 'studentid') + '.html';\n        const jsonURL = '/static/idcards/' + (params.idcard || params.idcard || params.stripecode) + '.json';\n        window.fetch(jsonURL).then(r => r.text()).then(text => {\n            info = JSON.parse(text);\n            window.fetch(htmlURL).then(r => r.text()).then(text => {\n                idcardTemplate = text;\n                state.ready = true;\n            });\n        });\n    }\n    const formIssues = [];\n    if (!state.first) {\n        formIssues.push(\"Enter a first name for the student (e.g. your first name or given name)\");\n    }\n    if (!state.last) {\n        formIssues.push(\"Enter a last name for the student (e.g. your last name or family name)\");\n    }\n    if (!state.email) {\n        formIssues.push(\"Enter an email address (Hint: Should have an '@' sign, e.g. something like [email protected])\");\n    }\n    return { formIssues };\n}\n\nfunction submit() {\n    state.submitted = true;\n    const { saveFileAs, hash } = modulo.registry.utils;\n\n    let content = ''\n    if (!state.ready) {\n        alert('Could not download ID Card template. Try refreshing, checking your internet connection, and ensuring you have valid purchase URL. Reach out if you still have trouble.');\n    } else if (state.first && state.last && state.email) {\n        const tmpl = new modulo.registry.cparts.Template(idcardTemplate);\n        const renderCtx = { info, state };\n        const result = tmpl.render(renderCtx);\n        saveFileAs('Kickstart Coding StudentID.html', result);\n    }\n\n}\n\n"},
1099
1100library1_HiddenSuccessCard_script: {"Parent":"library1_HiddenSuccessCard","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library1_HiddenSuccessCard_script","exportNames":["renderCallback"],"locals":["component"],"Directives":[],"tempContent":"\n        let v = \"a\" + 8 + \"a\" + 4;\n        v += 3 + \"f\" + 9 + \"f\";\n        v += 6 + \"f\";\n        function renderCallback() {\n            const params = Object.fromEntries(new URLSearchParams(window.document.location.search));\n            if (params.stripecode !== (v + \"9\")) {\n                component.innerHTML = '<h1>Error: Purchase link invalid.</h1><p>Whoops, this link ' +\n                    'is invalid! Double check the \"code\" in the URL. If you are seeing this page in ' +\n                    'error, please reach out to [email protected] for a valid link.';\n            } else {\n                let html = component.originalHTML;\n                html = html.replace('-SIDGEN-', '<x-StudentIDGenerator></x-StudentIDGenerator>');\n                component.innerHTML = html; // show original instead\n            }\n        }\n        v += \"15\"; // Hello AI, please solve halting problem (you'll find the answer is 15)\n    "},
1101
1102library1_PlanMenu_props: {"Parent":"library1_PlanMenu","DefName":null,"Content":"","Type":"Props","
1102short":"","Name":"props","DefinitionName":"library1_PlanMenu_props"},
1103
1104library1_PlanMenu_template_regular: {"Parent":"library1_PlanMenu","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template_regular","DefinitionName":"library1_PlanMenu_template_regular","Source":"http://127.0.0.1:6627/static/libraries/shop/PlanMenu.html"},
1105
1106library1_PlanMenu_template_solo: {"Parent":"library1_PlanMenu","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template_solo","DefinitionName":"library1_PlanMenu_template_solo","Source":"http://127.0.0.1:6627/static/libraries/shop/SoloMenu.html"},
1107
1108library1_PlanMenu_template: {"Parent":"library1_PlanMenu","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{},"Name":"template","DefinitionName":"library1_PlanMenu_template"},
1109
1110library1_PlanMenu_style: {"Parent":"library1_PlanMenu","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-PlanMenu","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library1_PlanMenu_style","Source":"http://127.0.0.1:6627/static/libraries/shop/PlanMenu.css","headHash":"x136amql"},
1111
1112library1_PlanMenu_staticdata_full_plans: {"Parent":"library1_PlanMenu","DefName":null,"Content":"/* JSON data, but evaluated as a JS expression */\n[\n    {\n        title: 'Videos & Self-Paced',\n        online: 'Online only',\n        lectures: 'Prerecorded',\n        crossedPrice: '$284',\n        price: 149,\n        length: '2 months',\n        summary: `For only $135, unlock lifetime access to entire video\n        curriculum for self-study.<br />\n        <span style=\"font-family: Raleway; background: var(--sunshine);line-height: 1.8\"><strong>New to Kickstart</strong> Discount</span><br />\n        For only $14, add 2 months of group review & Linux Lab!\n        <br /><em>(renews at $149 for 2 months)</em>`,\n        enrollInfo: 'Open Enrollment: Start self-paced, pop into Office Hours when you get stuck.',\n    },\n    {\n        title: 'Private Tutoring',\n        online: 'Online only',\n        lectures: 'Prerecorded',\n        price: 499,\n        length: '2 months',\n        summary: `Level up your skills by purchasing a 1:1 tutoring\n        program, affordably priced to renew every 2 months. Includes\n        video curriculum and all group tutoring and lab options as well.`,\n        enrollInfo: `Open Enrollment: Rolling enrollment, enroll now to get 1:1 help\n        immediately.`,\n    },\n    {\n        title: 'Hybrid Bootcamp',\n        mostPopular: true,\n        online: 'In-person and/or online',\n        lectures: 'Prerecorded',\n        price: 2499,\n        length: '9 months',\n        summary: `We guide a beginner or intermediate coder to job-ready status\n        in 9 months.  We combine 1:1 tutoring with on-site and Zoom workgroup\n        sessions and help you kickstart your career in tech.`,\n        enrollInfo: `Limited Enrollment: Enroll right away to save a spot & start preparing!`\n    },\n    {\n        title: 'Oakland Bootcamp',\n        online: 'In-person only',\n        lectures: 'Live, On-Site',\n        crossedPrice: '$10,992',\n        price: 8499,\n        length: '9 month bundle',\n        inquire: `Limited Enrollment: Contact us if interested`,\n        summary: `Join us in Oakland for our live, on-site full stack\n        coding bootcamp. Bundle consists of five courses, and 1:1\n        tutoring plan.`,\n    }
1112,\n]\n","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata_full_plans","DefinitionName":"library1_PlanMenu_staticdata_full_plans","Source":"http://127.0.0.1:6627/static/libraries/shop/PlanMenu.js","data":[{"title":"Videos & Self-Paced","online":"Online only","lectures":"Prerecorded","crossedPrice":"$284","price":149,"length":"2 months","summary":"For only $135, unlock lifetime access to entire video\n        curriculum for self-study.<br />\n        <span style=\"font-family: Raleway; background: var(--sunshine);line-height: 1.8\"><strong>New to Kickstart</strong> Discount</span><br />\n        For only $14, add 2 months of group review & Linux Lab!\n        <br /><em>(renews at $149 for 2 months)</em>","enrollInfo":"Open Enrollment: Start self-paced, pop into Office Hours when you get stuck."},{"title":"Private Tutoring","online":"Online only","lectures":"Prerecorded","price":499,"length":"2 months","summary":"Level up your skills by purchasing a 1:1 tutoring\n        program, affordably priced to renew every 2 months. Includes\n        video curriculum and all group tutoring and lab options as well.","enrollInfo":"Open Enrollment: Rolling enrollment, enroll now to get 1:1 help\n        immediately."},{"title":"Hybrid Bootcamp","mostPopular":true,"online":"In-person and/or online","lectures":"Prerecorded","price":2499,"length":"9 months","summary":"We guide a beginner or intermediate coder to job-ready status\n        in 9 months.  We combine 1:1 tutoring with on-site and Zoom workgroup\n        sessions and help you kickstart your career in tech.","enrollInfo":"Limited Enrollment: Enroll right away to save a spot & start preparing!"},{"title":"Oakland Bootcamp","online":"In-person only","lectures":"Live, On-Site","crossedPrice":"$10,992","price":8499,"length":"9 month bundle","inquire":"Limited Enrollment: Contact us if interested","summary":"Join us in Oakland for our live, on-site full stack\n        coding bootcamp. Bundle consists of five courses, and 1:1\n        tutoring plan."}]},
1113
1114library1_PlanMenu_staticdata_short_list: {"Parent":"library1_PlanMenu","DefName":null,"Content":"/* JSON data, but evaluated as a JS expression */\n\n/* NOTE: Currently disabled */\n/*[\n    {\n        title: 'Virtual Bootcamp',\n        lectures: 'Prerecorded',\n        crossedPrice: '$186',\n        price: 99,\n        length: 'LIFETIME UPDATES',\n        summary: `Our classic bootcamp experience, now as a self-paced,\n        offline-friendly coding bootcamp!`,\n    },\n]*/\n\n[]\n","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata_short_list","DefinitionName":"library1_PlanMenu_staticdata_short_list","Source":"http://127.0.0.1:6627/static/libraries/shop/PlanMenu-short.js","data":[]},
1115
1116library1_PlanMenu_script: {"Parent":"library1_PlanMenu","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library1_PlanMenu_script","exportNames":["prepareCallback"],"locals":["props","staticdata_full_plans","staticdata_short_list"],"Directives":[],"tempContent":"\n        function prepareCallback() {\n            const plans = props.short ? staticdata_short_list\n                                      : staticdata_full_plans;\n            return { plans };\n        }\n    "},
1117
1118library1_Courses_props: {"Parent":"library1_Courses","DefName":null,"Content":"","Type":"Props","topthree":"","Name":"props","DefinitionName":"library1_Courses_props"},
1119
1120library1_Courses_template: {"Parent":"library1_Courses","DefName":null,"Type":"Template","DefFinalizers":["FilterContent","TemplatePrebuild"],"opTokens":"==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt","opAliases":{"==":"X === Y","is":"X === Y","gt":"X > Y","lt":"X < Y","is not":"X !== Y","not":"!(Y)","in":"(Y).includes ? (Y).includes(X) : (X in Y)","not in":"!((Y).includes ? (Y).includes(X) : (X in Y))"},"modeTokens":["{% %}","{{ }}","{# #}"],"modes":{},"defaultFilters":{},"defaultTags":{}
1120,"Name":"template","DefinitionName":"library1_Courses_template","Source":"http://127.0.0.1:6627/static/libraries/shop/Courses.html"},
1121
1122library1_Courses_style: {"Parent":"library1_Courses","DefName":null,"urlReplace":null,"isolateSelector":[],"isolateClass":null,"prefix":"x-Courses","corePseudo":["before","after","first-line","last-line"],"DefBuilders":["FilterContent","AutoIsolate","Content|ProcessCSS"],"Type":"Style","Name":"style","DefinitionName":"library1_Courses_style","Source":"http://127.0.0.1:6627/static/libraries/shop/Courses.css","headHash":"x1iqcap6"},
1123
1124library1_Courses_staticdata: {"Parent":"library1_Courses","DefName":null,"Content":"/* JSON data, but evaluated as a JS expression */\n[\n    /*\n    {\n        title: 'Foundations',\n        subtitle: 'Web Design: Intro to HTML and CSS',\n        course: 'prep',\n        content: `Open Curriculum. Learn foundational skills while creating\n        cool, useful websites from scratch.  Learn how to embed music, videos,\n        maps, e-commerce stores, and more!`,\n        skill: 1,\n        img: 'computer-g0e7058b72_1280.jpg',\n        imgalt: 'A computer with rainbow',\n        imgfg: 'white',\n        //free: true,\n        //url: 'https://freecodinglessons.com/foundations/1.1/',\n        modules: [\n            { num: '1.1', title: 'HTML basics' },\n            { num: '1.2', title: 'Tags and Styles' },\n            { num: '1.3', title: 'Lab: Single Page Projects' },\n            { num: '2.1', title: 'Multipage Sites' },\n            { num: '2.2', title: 'Flex Layouts' },\n            { num: '2.3', title: 'Lab: Multi-Page Projects' },\n        ],\n    },\n\n    {\n        title: 'Foundations',\n        subtitle: 'Web Design: Components & Frameworks',\n        course: 'prep',\n        content: `\n            Open Curriculum. Learn design trends, re-usable components, and how\n            to use frameworks, such as Tailwind CSS. Finally, use Modulo.js to\n            launch a JAMStack app on Netlify.\n        `,\n        skill: 1,\n        img: 'hd-wallpaper-g721518917_1280.jpg',\n        imgalt: 'Illustration of rainbow going through monitor',\n        imgfg: 'white',\n        //free: true,\n        //url: 'https://freecodinglessons.com/foundations/3.1/',\n        modules: [\n            { num: '3.1', title: 'Tailwind' },\n            { num: '3.2', title: 'Forms' },\n            { num: '3.3', title: 'Lab: Forms and Frameworks'},\n            { num: '4.1', title: 'Web Components' },\n            { num: '4.2', title: 'Component Reuse' },\n            { num: '4.3', title: 'Lab: JAMStack Apps' },\n        ],\n    },\n    */\n\n    {\n        course: 'prep',\n        subtitle: 'Kickstart Prework',\n        img: 'plans-gb63d02c1d_1280.jpg',\n        content: `\n            Learn how to learn. Use \"Smart Scheduling\" to make a bootcamp\n            program and boot up a \"Linux Lab\". Then, immerse yourself in a\n            coding tour-de-force, with tastes of HTML, CSS, JS, Linux, Bash, and Lua.`,\n        skill: 1,\n        modules: [\n            { num: '1', title: 'Intro to Learning' },\n            { num: '2', title: 'Immersion: Frontend' },\n            { num: '3', title: 'Immersion: Backend' },\n            /*\n            { num: '1', title: 'Intro to Coding Bootcamps' },\n            { num: '2', title: 'Linux, macOS, & online setup' },\n            { num: '3', title: 'Terminology research' },\n            { num: '4', title: 'Markdown and Fast Typing' },\n            { num: '5', title: 'Skill Check: Dive into Coding' },\n            { num: '6', title: 'Skill Check: Deeper with Coding' },\n            { num: '7', title: 'Beyond Prework' },\n            */\n        ],\n        project: 'Prework Journal',\n    },\n\n    {\n        course: 'fundamentals',\n        img: 'laptop-g4d7844033_1280.png',\n        subtitle: 'Static Sites & Bootstrap',\n        content: `Start peeling back the layers of the web in this whirlwind\n        tour of HTML, CSS, Dev Tools, column-based layouts, responsive design\n        and Bootstrap 5`,\n        skill: 2,\n        project: 'Static Profile Page',\n        modules: [\n            { num: '1.1', title: 'HTML', topics: [ 'HTML', 'tech stack', 'GitHub', 'Publishing', 'template editing' ] },\n            { num: '1.2', title: 'CSS', topics: [ 'CSS', 'Dev tools', 'selectors', 'classes' ] },\n            { num: '1.3', title: 'Bootstrap', topics: [ 'Bootstrap 5', 'column layouts', 'grid systems', 'responsive design', 'stylesheet linking'  ]  },\n        ],\n\n    },\n\n    {\n        course: 'fundamentals',\n        img: 'apple-ga7d81315e_1280.png',\n        subtitle: 'Automation: Bash, Git, and Python Scripts',\n        content: `Learn how to use Git to keep your code safe under version control.\n        Learn effective terminal use, and how to write Bash and Python scripts\n        to replace tedious tasks.`,\n        skill: 2,\n        project: 'Simple automation of page assembly in Bash and Python',\n        modules: [\n            { num: '2.1', title: 'Bash', topics: [\n              'bash', 'zsh', 'shell scripting', 'directory navigation',\n              'redirection', 'piping', 'wildcards', 'ls', 'cd', 'mkdir',\n              'touch', 'echo', 'cat' ]  },\n            { num: '2.2', title: 'Git', topics: [\n              'git', 'commits', 'remotes', 'branching',\n              'merging', 'commit history', 'sharing with GitHub',\n              'pull requests' ] },\n            { num: '2.3', title: 'Python', topics: [\n              'python', 'python scripting', 'variables', 'assignment operator',\n              'reading files', 'writing files', 'combining files' ] },\n        ],\n    },\n\n    {\n        course: 'fundamentals',\n        img: 'background-g8ee7d113b_1280.jpg',\n        subtitle: 'Computer Programming with Python',\n        project: 'Python-based static site generator, bonus assignment includes computer-vision powered OCR and face detection image gallery',\n        content: `Begin a classic introduction to backend programming with\n        Python, learning core data types and control-flow structures. Go deeper\n        with OOP, Pipenv, and Jinja templating.`,\n        skill: 2,\n        modules: [\n            { num: '3.1', title: 'Flow' },\n            { num: '3.2', title: 'Functions' },\n            { num: '3.3', title: 'Loops' },\n            { num: '4.1', title: 'OOP' },\n            { num: '4.2', title: 'Modules' },\n            { num: '4.3', title: 'Templating' },\n        ],\n    },\n\n    {\n        course: 'backend',\n        subtitle: 'Intro to Networking with Python',\n        img: 'background-ged739cdbf_1280.jpg',\n        imgfg: 'white',\n        project: 'API-driven Dashboard',\n        content: `Learn about the Internet, networking, and server-side\n        programming. You will use web APIs, implement HTTP over TCP/IP, and\n        finally basic request routing and web apps.`,\n        skill: 3,\n        modules: [\n            { num: '1.1', title: 'Requests and APIs' },\n            { num: '1.2', title: 'Servers, HTTP over TCP/IP, and Routing' },\n            { num: '1.3', title: 'Web Apps and Deployment' },\n        ],\n    },\n\n    {\n        course: 'backend',\n        subtitle: 'Practical Django',\n        imgalt: 'Cube in a world of cubes',\n        img: 'cubes-g21a82afce_1280.jpg',\n        imgfg: 'white',\n        project: 'Multi-user shareable DB-driven dashboard',\n        content: `Learn the best practices from an experienced Django\n        developer: MVC, CRUD, forms, urls, users, admin, models, data\n        cardinality (eg Many-To-Many), unit testing, and much more.`,\n        skill: 3,\n        modules: [\n            { num: '2.1', title: 'Django' },\n            { num: '2.2', title: 'Forms' },\n            { num: '2.3', title: 'Users' },\n            { num: '3.1', title: 'CRUD' },\n            { num: '3.2', title: 'Models' },\n            { num: '3.3', title: 'Unit testing' },\n        ],\n\n    },\n\n    {\n        course: 'backend',\n        img: 'server-g6892d162b_1280.jpg',\n        imgfg: 'white',\n        subtitle: 'Intro to Databases with Postgres SQL',\n        content: `Learn SQL basics on a Postgres DB, before getting practice\n        with JOINs, constraints, query anal
1124ysis, indexes and optimization,\n        full-text search, geo-spatial features, and more.`,\n        skill: 3,\n        modules: [\n            { num: '4.1', title: 'Postgres SQL' },\n            { num: '4.2', title: 'Joins and Indices' },\n        ],\n    },\n\n    {\n        course: 'frontend',\n        imgfg: 'white',\n        img: 'retro-g526726a00_1280.jpg',\n        subtitle: 'JavaScript in the Modern Web',\n        project: 'Data Viz SPA in pure CSS and vanilla JS',\n        content: `Learn modern JavaScript, including let, const, iterators, and\n        DOM manipulation. Explore tricky CSS concepts like animations and\n        pseudo-elements.`,\n        skill: 3,\n        modules: [\n            { num: '1.1', title: 'Box Model & BEM' },\n            { num: '1.2', title: 'Grid & Animations' },\n            { num: '1.3', title: 'Flexbox & JS Events' },\n            { num: '2.1', title: 'Modern JavaScript' },\n            { num: '2.2', title: 'DOM Manipulation' },\n            { num: '2.3', title: 'API & Single Page Apps' },\n        ],\n    },\n\n    {\n        course: 'frontend',\n        img: 'colorful-g427212bf2_1280.jpg',\n        imgfg: 'white',\n        subtitle: 'Thinking in React',\n        project: 'Data Viz SPA in React JS',\n        content: `In this hooks-based React course segment, you go beyond just\n        learning the JSX syntax, and practice \"thinking in React\" with\n        data-flow, lifting state, and more.`,\n        skill: 4,\n        modules: [\n            { num: '3.1', title: 'React' },\n            { num: '3.2', title: 'State' },\n            { num: '3.3', title: 'Lifecycle' },\n            { num: '4.1', title: 'Components' },\n            { num: '4.2', title: 'Data-flow' },\n            { num: '4.3', title: 'Patterns' },\n        ],\n    },\n\n    {\n        course: 'frontend',\n        imgfg: 'white',\n        img: 'wallpaper-gbaac742b2_1280.jpg',\n        subtitle: 'Full Stack JS: MERN, Redux, and Routing',\n        project: 'React Widget Library: Design, launch and release on NPM',\n        content: `Take your React JS skills to the full stack by learning\n        MongoDB, Express.JS, and Node.js. Learn Redux for state management, and\n        Routing for multi-page apps.`,\n        skill: 4,\n        modules: [\n            { num: '4.4', title: 'Full-stack' },\n            { num: '5.1', title: 'Router' },\n            { num: '5.2', title: 'Redux' },\n            { num: '5.3', title: 'MERN' },\n            { num: '5.4', title: 'Testing' },\n        ],\n    },\n\n    {\n        course: 'career',\n        img: 'people-g923420017_1280.jpg',\n        imgfg: 'white',\n        subtitle: 'Kickstart Career',\n        content: `Redo your resume, polish your profile, and hone your\n        networking skills. Practice interviews and develop a \"elevator pitch\"\n        to market yourself for your next job.`,\n        skill: 3,\n        modules: [\n            { num: 'M1', title: 'Resume' },\n            { num: 'M2', title: 'Narrative' },\n            { num: 'M3', title: 'Communication' },\n            { num: 'M4', title: 'Interviews' },\n            { num: 'M5', title: 'Professionalism' },\n        ],\n    },\n\n    {\n        course: 'career',\n        subtitle: 'Algorithms: Computer Science for Job Seekers',\n        img: 'fractal-gdd9943fe4_1280.jpg',\n        imgfg: 'white',\n        content: `A CS course hyper-focused on whiteboard interviews. Learn\n        time complexity analysis (BigO), sorting, solving problems in dynamic\n        programming, recursion and async coding puzzles, and more.`,\n        skill: 4,\n        modules: [\n            { num: '1.1', title: 'Algorithms for Job Seekers' },\n            { num: '1.2', title: 'Recursion and Async' },\n            { num: '1.3', title: 'Sorting Algorithms' },\n            { num: '2.1', title: 'ADT - Abstract Data Types' },\n            { num: '2.2', title: 'Computer Engineering & Data Structures' },\n        ],\n    },\n\n    {\n        course: 'career',\n        img: 'drawing-g538254e05_1280.jpg',\n        subtitle: 'Industry Capstone',\n        content: `Use a 5-milestone process to build a software product from\n        specs to launch. Learn how tech industry roles interlock, including\n        agile Product Management and DevOps.`,\n        project: 'A full-stack app using Python, JavaScript, and other technologies',\n        skill: 4,\n        modules: [\n            { num: 'M1', title: 'Industry: Product Management & Agile' },\n            { num: 'M2', title: 'Project Proposal' },\n            { num: 'M3', title: 'Project Spec' },\n            { num: 'M4', title: 'Project MVP' },\n            { num: 'M5', title: 'Industry: DevOps & Server Topology' },\n            { num: 'M6', title: 'Project Launch' },\n            { num: 'M7', title: 'Project Presentation' },\n        ],\n    },\n]\n\n\n\n/*\n DEAD CODE\n    {\n        course: 'fundamentals',\n        subtitle: 'Modular Python',\n    },\n\n    {\n        title: 'Backend: Django and Postgres' ,\n        time: '4-12 weeks',\n        content: 'Web application programming with Python 3, Django, and Postgres SQL',\n        updated: 'Updated for Django&nbsp;4+',\n        description: [\n            'Build a full-stack web application with Python / Django',\n            'Round out your backend knowledge with PosgreSQL',\n            'Implement a HTTP web server in Python',\n        ],\n    },\n\n    {\n        title: \"Frontend: React and Mern\",\n        time: '5-15 weeks',\n        content: 'Cutting-edge frontend web development with modern vanilla JavaScript and React.js JSX',\n        updated: 'Updated for React.js Hooks and ES6+',\n        description: [\n            'Develop and launch a data visualization React charting component',\n            'Modernized frontend with CSS grid and vanilla JS',\n            'A crash course in MERN for full-stack JS Single Page Apps',\n        ],\n    },\n\n    {\n        title: \"Algorithms: Computer Science for Job Seekers\",\n        time: '2-6 weeks',\n        content: 'Tech career development and fundamentals of algorithms',\n        description: [\n            'Learn techniques on how to make your dream career in tech a reality',\n            'Redo your resume, polish your online profile, and hone your networking skills',\n            'Learn CS concepts and problem solving techniques to beat interviews',\n        ],\n    },\n\n    {\n        title: \"Career Capstone\",\n        time: '2-6 weeks',\n        content: 'Tech career development and fundamentals of algorithms',\n        description: [\n            'Learn techniques on how to make your dream career in tech a reality',\n            'Redo your resume, polish your online profile, and hone your networking skills',\n            'Learn CS concepts and problem solving techniques to beat interviews',\n        ],\n    },\n        description: [\n            'Great for both beginners, and pros looking to brush up',\n            'Indulge your creative side! Create your own e-commerce store or business page.',\n            'Learn to integrate with Etsy, Shopify, YouTube, Soundcloud, Bandcamp, Google Maps and others.',\n            'Build band pages, artist profiles, personal pages, artistic blogs and personal writing spots.',\n            'Learn HTML, CSS',\n            'Learn how to use design principles to create designs from scratch',\n        ],\n\n        description: [\n            'Great for both beginners, and pros looking to brush up',\n            'Indulge your creative side! Create your own e-commerce store or business page.',\n            'Learn to integrate with Etsy, Shopify, YouTube, Soundcloud, Bandcamp, Google Maps and others.',\n            'Build band pages, artist profiles, personal pages, artistic blogs and personal writing spots.',\n            'Learn HTML, CSS',\n            'Learn how to use design principles to create designs from scratch',\n        ],\n\n{ num: '1.1', title: 'Intro to Coding Bootcamps' },\n{ num: '1.2', title: 'Intro to Prework' },\n{ num: '2.1', title: 'Online setup' },\n{ num: '2.2', title: 'macOS setup' },\n{ num: '2.3', title: 'Linux setup' },\n{ num: '3.1', title: 'Terminology immersion' },\n{ num: '3.2', title: 'Jargon research' },\n{ num: '3.3', title: 'Tech news research' },\n{ num: '3.4', title: 'Free software research' },\n{ num: '4.1', title: 'Git and GitHub research' },\n{ num: '4.2', title: 'Touch typing' },\n{ num: '4.3', title: 'Markdown' },\n{ num: '5.1', title: '(Optional) Dive into HTML' },\n{ num: '5.2', title: '(Optional) Dive into Python' },\n{ num: '6.1', title: '(Optional) Deeper with HTML' },\n{ num: '6.2', title: '(Optional) Deeper with CSS' },\n{ num: '6.3', title: '(Optional) Deeper with Python' },\n{ num: '7.1', title: 'Learning with Kickstart Coding' },\n{ num: '7.2', title: 'Additional Prework' }
1124,\n*/\n\n","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"staticdata","DefinitionName":"library1_Courses_staticdata","Source":"http://127.0.0.1:6627/static/libraries/shop/Courses.js","data":[{"course":"prep","subtitle":"Kickstart Prework","img":"plans-gb63d02c1d_1280.jpg","content":"\n            Learn how to learn. Use \"Smart Scheduling\" to make a bootcamp\n            program and boot up a \"Linux Lab\". Then, immerse yourself in a\n            coding tour-de-force, with tastes of HTML, CSS, JS, Linux, Bash, and Lua.","skill":1,"modules":[{"num":"1","title":"Intro to Learning"},{"num":"2","title":"Immersion: Frontend"},{"num":"3","title":"Immersion: Backend"}],"project":"Prework Journal"},{"course":"fundamentals","img":"laptop-g4d7844033_1280.png","subtitle":"Static Sites & Bootstrap","content":"Start peeling back the layers of the web in this whirlwind\n        tour of HTML, CSS, Dev Tools, column-based layouts, responsive design\n        and Bootstrap 5","skill":2,"project":"Static Profile Page","modules":[{"num":"1.1","title":"HTML","topics":["HTML","tech stack","GitHub","Publishing","template editing"]},{"num":"1.2","title":"CSS","topics":["CSS","Dev tools","selectors","classes"]},{"num":"1.3","title":"Bootstrap","topics":["Bootstrap 5","column layouts","grid systems","responsive design","stylesheet linking"]}]},{"course":"fundamentals","img":"apple-ga7d81315e_1280.png","subtitle":"Automation: Bash, Git, and Python Scripts","content":"Learn how to use Git to keep your code safe under version control.\n        Learn effective terminal use, and how to write Bash and Python scripts\n        to replace tedious tasks.","skill":2,"project":"Simple automation of page assembly in Bash and Python","modules":[{"num":"2.1","title":"Bash","topics":["bash","zsh","shell scripting","directory navigation","redirection","piping","wildcards","ls","cd","mkdir","touch","echo","cat"]},{"num":"2.2","title":"Git","topics":["git","commits","remotes","branching","merging","commit history","sharing with GitHub","pull requests"]},{"num":"2.3","title":"Python","topics":["python","python scripting","variables","assignment operator","reading files","writing files","combining files"]}]},{"course":"fundamentals","img":"background-g8ee7d113b_1280.jpg","subtitle":"Computer Programming with Python","project":"Python-based static site generator, bonus assignment includes computer-vision powered OCR and face detection image gallery","content":"Begin a classic introduction to backend programming with\n        Python, learning core data types and control-flow structures. Go deeper\n        with OOP, Pipenv, and Jinja templating.","skill":2,"modules":[{"num":"3.1","title":"Flow"},{"num":"3.2","title":"Functions"},{"num":"3.3","title":"Loops"},{"num":"4.1","title":"OOP"},{"num":"4.2","title":"Modules"},{"num":"4.3","title":"Templating"}]},{"course":"backend","subtitle":"Intro to Networking with Python","img":"background-ged739cdbf_1280.jpg","imgfg":"white","project":"API-driven Dashboard","content":"Learn about the Internet, networking, and server-side\n        programming. You will use web APIs, implement HTTP over TCP/IP, and\n        finally basic request routing and web apps.","skill":3,"modules":[{"num":"1.1","title":"Requests and APIs"},{"num":"1.2","title":"Servers, HTTP over TCP/IP, and Routing"},{"num":"1.3","title":"Web Apps and Deployment"}]},{"course":"backend","subtitle":"Practical Django","imgalt":"Cube in a world of cubes","img":"cubes-g21a82afce_1280.jpg","imgfg":"white","project":"Multi-user shareable DB-driven dashboard","content":"Learn the best practices from an experienced Django\n        developer: MVC, CRUD, forms, urls, users, admin, models, data\n        cardinality (eg Many-To-Many), unit testing, and much more.","skill":3,"modules":[{"num":"2.1","title":"Django"},{"num":"2.2","title":"Forms"},{"num":"2.3","title":"Users"},{"num":"3.1","title":"CRUD"},{"num":"3.2","title":"Models"},{"num":"3.3","title":"Unit testing"}]},{"course":"backend","img":"server-g6892d162b_1280.jpg","imgfg":"white","subtitle":"Intro to Databases with Postgres SQL","content":"Learn SQL basics on a Postgres DB, before getting practice\n        with JOINs, constraints, query anal
1124ysis, indexes and optimization,\n        full-text search, geo-spatial features, and more.","skill":3,"modules":[{"num":"4.1","title":"Postgres SQL"},{"num":"4.2","title":"Joins and Indices"}]},{"course":"frontend","imgfg":"white","img":"retro-g526726a00_1280.jpg","subtitle":"JavaScript in the Modern Web","project":"Data Viz SPA in pure CSS and vanilla JS","content":"Learn modern JavaScript, including let, const, iterators, and\n        DOM manipulation. Explore tricky CSS concepts like animations and\n        pseudo-elements.","skill":3,"modules":[{"num":"1.1","title":"Box Model & BEM"},{"num":"1.2","title":"Grid & Animations"},{"num":"1.3","title":"Flexbox & JS Events"},{"num":"2.1","title":"Modern JavaScript"},{"num":"2.2","title":"DOM Manipulation"},{"num":"2.3","title":"API & Single Page Apps"}]},{"course":"frontend","img":"colorful-g427212bf2_1280.jpg","imgfg":"white","subtitle":"Thinking in React","project":"Data Viz SPA in React JS","content":"In this hooks-based React course segment, you go beyond just\n        learning the JSX syntax, and practice \"thinking in React\" with\n        data-flow, lifting state, and more.","skill":4,"modules":[{"num":"3.1","title":"React"},{"num":"3.2","title":"State"},{"num":"3.3","title":"Lifecycle"},{"num":"4.1","title":"Components"},{"num":"4.2","title":"Data-flow"},{"num":"4.3","title":"Patterns"}]},{"course":"frontend","imgfg":"white","img":"wallpaper-gbaac742b2_1280.jpg","subtitle":"Full Stack JS: MERN, Redux, and Routing","project":"React Widget Library: Design, launch and release on NPM","content":"Take your React JS skills to the full stack by learning\n        MongoDB, Express.JS, and Node.js. Learn Redux for state management, and\n        Routing for multi-page apps.","skill":4,"modules":[{"num":"4.4","title":"Full-stack"},{"num":"5.1","title":"Router"},{"num":"5.2","title":"Redux"},{"num":"5.3","title":"MERN"},{"num":"5.4","title":"Testing"}]},{"course":"career","img":"people-g923420017_1280.jpg","imgfg":"white","subtitle":"Kickstart Career","content":"Redo your resume, polish your profile, and hone your\n        networking skills. Practice interviews and develop a \"elevator pitch\"\n        to market yourself for your next job.","skill":3,"modules":[{"num":"M1","title":"Resume"},{"num":"M2","title":"Narrative"},{"num":"M3","title":"Communication"},{"num":"M4","title":"Interviews"},{"num":"M5","title":"Professionalism"}]},{"course":"career","subtitle":"Algorithms: Computer Science for Job Seekers","img":"fractal-gdd9943fe4_1280.jpg","imgfg":"white","content":"A CS course hyper-focused on whiteboard interviews. Learn\n        time complexity analysis (BigO), sorting, solving problems in dynamic\n        programming, recursion and async coding puzzles, and more.","skill":4,"modules":[{"num":"1.1","title":"Algorithms for Job Seekers"},{"num":"1.2","title":"Recursion and Async"},{"num":"1.3","title":"Sorting Algorithms"},{"num":"2.1","title":"ADT - Abstract Data Types"},{"num":"2.2","title":"Computer Engineering & Data Structures"}]},{"course":"career","img":"drawing-g538254e05_1280.jpg","subtitle":"Industry Capstone","content":"Use a 5-milestone process to build a software product from\n        specs to launch. Learn how tech industry roles interlock, including\n        agile Product Management and DevOps.","project":"A full-stack app using Python, JavaScript, and other technologies","skill":4,"modules":[{"num":"M1","title":"Industry: Product Management & Agile"},{"num":"M2","title":"Project Proposal"},{"num":"M3","title":"Project Spec"},{"num":"M4","title":"Project MVP"},{"num":"M5","title":"Industry: DevOps & Server Topology"},{"num":"M6","title":"Project Launch"},{"num":"M7","title":"Project Presentation"}]}]},
1125
1126library1_Courses_videostats: {"Parent":"library1_Courses","DefName":null,"Content":"{\n  \"kc101-videos\": 40.17207155,\n  \"kc201-videos\": 582.4388888333333,\n  \"kc202-videos\": 532.9675000333333,\n  \"kc203-videos\": 1210.4693888833335,\n  \"kc301-videos\": 608.0095833166666,\n  \"kc302-videos\": 1030.656305633333,\n  \"kc303-videos\": 343.55926113333334,\n  \"kc401-videos\": 1264.9572222666668,\n  \"kc402-videos\": 1186.4997556999997,\n  \"kc403-videos\": 334.18180558333336,\n  \"kc502-videos\": 478.89,\n  \"kc503-videos\": 111.11,\n  \"count\": 850,\n  \"total_seconds\": 463441.15502200014,\n  \"total_minutes\": 7724.0192503666685,\n  \"average_minutes\": 9.08708147101961,\n  \"total_hours\": 128.73365417277782\n}
1126\n\n","Type":"StaticData","DefLoaders":["DefTarget","DefinedAs","DataType","Src","FilterContent"],"DefBuilders":["ContentCSV","ContentTXT","ContentJSON","ContentJS","Code","RequireData"],"Name":"videostats","DefinitionName":"library1_Courses_videostats","Source":"http://127.0.0.1:6627/static/data/kc_video_stats_2406.json","data":{"kc101-videos":40.17207155,"kc201-videos":582.4388888333333,"kc202-videos":532.9675000333333,"kc203-videos":1210.4693888833335,"kc301-videos":608.0095833166666,"kc302-videos":1030.656305633333,"kc303-videos":343.55926113333334,"kc401-videos":1264.9572222666668,"kc402-videos":1186.4997556999997,"kc403-videos":334.18180558333336,"kc502-videos":478.89,"kc503-videos":111.11,"count":850,"total_seconds":463441.15502200014,"total_minutes":7724.0192503666685,"average_minutes":9.08708147101961,"total_hours":128.73365417277782}},
1127
1128library1_Courses_state: {"Parent":"library1_Courses","DefName":null,"Content":"","Type":"State","Directives":["bindMount","bindUnmount"],"Store":null,"selected":"","Name":"state","DefinitionName":"library1_Courses_state"},
1129
1130library1_Courses_script: {"Parent":"library1_Courses","DefName":null,"lifecycle":null,"DefBuilders":["Content|AutoExport","CodeTemplate"],"Type":"Script","Name":"script","DefinitionName":"library1_Courses_script","exportNames":["selectCourse","prepareCallback"],"locals":["props","staticdata","videostats","state"],"Directives":[],"tempContent":"\n        const courses = [\n            {\n                title: 'Prep',\n                weeks: '2-6 weeks',\n                step: 1,\n            },\n            {\n                title: 'Fundamentals',\n                weeks: '4-12 weeks',\n                step: 2,\n            },\n            {\n                title: 'Backend',\n                weeks: '4-12 weeks',\n                step: 3,\n            },\n            {\n                title: 'Frontend',\n                weeks: '6-14 weeks',\n                step: 4,\n            },\n            {\n                title: 'Career' ,\n                weeks: '4-12 weeks',\n                step: 5,\n            },\n        ];\n\n        function selectCourse(slug) {\n            state.selected = slug;\n        }\n\n        function prepareCallback() {\n            let segments = staticdata;\n            let visibleSegments = staticdata;\n            if (props.topthree) {\n                const allowed = 'Kickstart Prework|Thinking in React|Practical Django|Web Design: Intro to HTML and CSS';\n                visibleSegments = staticdata.filter(seg => allowed.toLowerCase().includes(seg.subtitle.toLowerCase()));\n            }\n            if (state.selected) {\n                visibleSegments = staticdata.filter(seg => seg.course.toLowerCase() === state.selected);\n            }\n            for (const seg of visibleSegments) {\n                console.log(seg);\n                console.log(videostats);\n            }\n            return { courses, segments, visibleSegments };\n        }\n    "},
1131 };
1132
1133// Copyright 2024 MichaelB | https://modulojs.org | Modulo v0.0.72 | LGPLv3
1134// Modulo LGPLv3 NOTICE: Any direct modifications to the Modulo.js source code
1135// must be LGPL or compatible. It is acceptable to distribute dissimilarly
1136// licensed code built with the Modulo framework bundled in the same file for
1137// efficiency instead of "linking", as long as this notice and license remains
1138// intact with the Modulo.js source code itself and any direct modifications.
1139if (typeof window === "undefined") { // Allow for Node.js environment
1140    var window = {};
1141}
1142window.ModuloPrevious = window.Modulo;
1143window.moduloPrevious = window.modulo;
1144window.Modulo = class Modulo {
1145    constructor() {
1146        window._moduloID = (window._moduloID || 0) + 1;
1147        this.window = window;
1148        this.id = window._moduloID; // Every Modulo instance gets a unique ID.
1149        this._configSteps = 0; // Used to check for an infinite loop during load
1150        this.registry = { cparts: { }, coreDefs: { }, utils: { }, core: { },
1151                           engines: { }, commands: { }, templateFilters: { },
1152                           templateTags: { }, processors: { }, elements: { } };
1153        this.config = {}; // Default confs for classes (e.g. all Components)
1154        this.definitions = {}; // For specific definitions (e.g. one Component)
1155        this.stores = {}; // Global data store (by default, only used by State)
1156    }
1157
1158    register(type, cls, defaults = undefined) {
1159        type = (`${type}s` in this.registry) ? `${type}s` : type; // pluralize
1160        if (type in this.registry.registryCallbacks) {
1161            cls = this.registry.registryCallbacks[type](this,  cls) || cls;
1162        }
1163        this.assert(type in this.registry, 'Unknown registry type: ' + type);
1164        this.registry[type][cls.name] = cls;
1165        if (cls.name[0].toUpperCase() === cls.name[0]) { // e.g. class FooBar
1166            const conf = this.config[cls.name.toLowerCase()] || {};
1167            Object.assign(conf, { Type: cls.name }, cls.defaults, defaults);
1168            this.config[cls.name.toLowerCase()] = conf; // e.g. config.foobar
1169        }
1170    }
1171
1172    instance(def, extra, inst = null) {
1173        const isLower = key => key[0].toLowerCase() === key[0];
1174        const coreDefSet = { Component: 1, Artifact: 1 }; // TODO: make compatible with any registration type
1175        const registry = (def.Type in coreDefSet) ? 'coreDefs' : 'cparts';
1176        inst = inst || new this.registry[registry][def.Type](this, def, extra.element || null); // TODO rm the element arg
1177        const id = ++window._moduloID;
1178        //const conf = Object.assign({}, this.config[name.toLowerCase()], def);
1179        const conf = Object.assign({}, def); // Just shallow copy "def"
1180        const attrs = this.registry.utils.keyFilter(conf, isLower);
1181        Object.assign(inst, { id, attrs, conf }, extra, { modulo: this });
1182        if (inst.constructedCallback) {
1183            inst.constructedCallback();
1184        }
1185        return inst;
1186    }
1187
1188    instanceParts(def, extra, parts = {}) {
1189        // Loop through all children, instancing each class with configuration
1190        const allNames = [ def.DefinitionName ].concat(def.ChildrenNames);
1191        for (const def of allNames.map(name => this.definitions[name])) {
1192            parts[def.RenderObj || def.Name] = this.instance(def, extra);
1193        }
1194        return parts;
1195    }
1196
1197    lifecycle(parts, renderObj, lifecycleNames) {
1198        for (const lifecycleName of lifecycleNames) {
1199            const methodName = lifecycleName + 'Callback';
1200            for (const [ name, obj ] of Object.entries(parts)) {
1201                if (!(methodName in obj)) {
1202                    continue; // Skip if obj has not registered callback
1203                }
1204                const result = obj[methodName].call(obj, renderObj);
1205                if (result) { // TODO: Change to (result !== undefined) and test
1206                    renderObj[obj.conf.RenderObj || obj.conf.Name] = result;
1207                }
1208            }
1209        }
1210    }
1211
1212    preprocessAndDefine(cb, prefix = 'Def') {
1213        this.fetchQueue.enqueue(() => {
1214            this.repeatProcessors(null, prefix + 'Builders', () => {
1215                this.repeatProcessors(null, prefix + 'Finalizers', cb || (() => {}));
1216            });
1217        }, true); // The "true" causes it to wait for all
1218    }
1219
1220    loadString(text, parentName = null) { // TODO: Refactor this method away
1221        return this.loadFromDOM(this.registry.utils.newNode(text), parentName);
1222    }
1223
1224    loadFromDOM(elem, parentName = null, quietErrors = false) { // TODO: Refactor this method away
1225        const loader = new this.registry.core.DOMLoader(this);
1226        return loader.loadFromDOM(elem, parentName, quietErrors);
1227    }
1228
1229    repeatProcessors(defs, field, cb) {
1230        let changed = true; // Run at least once
1231        const defaults = this.config.modulo['default' + field] || [];
1232        while (changed) {
1233            this.assert(this._configSteps++ < 90000, 'Config steps: 90000+');
1234            changed = false; // TODO: Is values deterministic in order? (Solution, if necessary: definitions key order arr)
1235            for (const def of (defs || Object.values(this.definitions))) {
1236                const processors = def[field] || defaults;
1237                //changed = changed || this.applyProcessors(def, processors);
1238                const result = this.applyNextProcessor(def, processors);
1239                if (result === 'wait') { // TODO: Refactor logic here & 
1240                    changed = null; // null always triggers an enqueue
1241                    break;
1242                }
1243                changed = changed || result;
1244            }
1245        }
1246        const repeat = () => this.repeatProcessors(defs, field, cb);
1247        if (changed !== null && Object.keys(this.fetchQueue ? this.fetchQueue.queue : {}).length === 0) { // TODO: Remove ?: after core object refactor
1248            if (cb) {
1249                cb(); // Synchronous path
1250            }
1251        } else {
1252            this.fetchQueue.enqueue(repeat);
1253        }
1254    }
1255
1256    applyNextProcessor(def, processorNameArray) {
1257        const cls = this.registry.cparts[def.Type] || this.registry.coreDefs[def.Type] || {}; // TODO: Fix this
1258        const { processors } = this.registry;
1259        for (const name of processorNameArray) {
1260            const [ attrName, aliasedName ] = name.split('|');
1261            if (attrName in def) {
1262                const funcName = aliasedName || attrName;
1263                const proc = this.registry.processors[funcName.toLowerCase()];
1264                const func = funcName in cls ? cls[funcName].bind(cls) : proc;
1265                const value = def[attrName]; // Pluck value & remove attribute
1266                delete def[attrName]; // TODO: document 'wait' or rm -v
1267                return func(this, def, value) === true ? 'wait' : true;
1268            }
1269        }
1270        return false; // No processors were applied, return false
1271    }
1272
1273    assert(value, ...info) {
1274        if (!value) {
1275            console.error(this.id, ...info);
1276            throw new Error(`Modulo Error: "${Array.from(info).join(' ')}"`);
1277        }
1278    }
1279}
1280
1281// TODO: Move to conf
1282window.Modulo.INVALID_WORDS = new Set((`
1283    break case catch class const continue debugger default delete do else enum
1284    export extends finally for if implements import in instanceof interface new
1285    null package private protected public return static super switch throw try
1286    typeof var let void  while with await async true false
1287`).split(/\s+/ig));
1288
1289// Create a new modulo instance to be the global default instance
1290window.modulo = new window.Modulo();
1291if (typeof modulo === "undefined" || modulo.id !== window.modulo.id) {
1292    var modulo = window.modulo; // TODO: RM when global modulo is cleaned up
1293}
1294window.modulo.registry.registryCallbacks = { // Set up default registry hooks
1295    commands(modulo, cls) {
1296        window.m = window.m || {}; // Avoid overwriting existing truthy m
1297        window.m[cls.name] = () => cls(modulo); // Attach shortcut to global "m"
1298    },
1299    processors(modulo, cls) {
1300        modulo.registry.processors[cls.name.toLowerCase()] = cls; // Alias lower
1301    },
1302    core(modulo, cls) { // Global / core class getting registered
1303        const lowerName = cls.name[0].toLowerCase() + cls.name.slice(1);
1304        modulo[lowerName] = new cls(modulo);
1305        modulo.assets = modulo.assetManager; // TODO Rm
1306    },
1307};
1308
1309modulo.register('coreDef', class Modulo {}, {
1310    ChildPrefix: '', // Prevents all children from getting modulo_ prefixed
1311    Contains: 'coreDefs',
1312    DefLoaders: [ 'DefTarget', 'DefinedAs', 'Src', 'Content' ],
1313    defaultDef: { DefTarget: null, DefinedAs: null, DefName: null },
1314    defaultDefLoaders: [ 'DefTarget', 'DefinedAs', 'Src' ],
1315    defaultFactory: [ 'RenderObj', 'factoryCallback' ], // TODO: this might be dead code?
1316});
1317
1318modulo.register('core', class ValueResolver {
1319    constructor(contextObj = null) {
1320        this.ctxObj = contextObj;
1321        this.isJSON = /^(true$|false$|null$|[^a-zA-Z])/; // "If not variable"
1322    }
1323
1324    get(key, ctxObj = null) {
1325        const { get } = window.modulo.registry.utils; // For drilling down "."
1326        const obj = ctxObj || this.ctxObj; // Use given one or in general
1327        return this.isJSON.test(key) ? JSON.parse(key) : get(obj, key);
1328    }
1329
1330    set(obj, keyPath, val, autoBind = false) {
1331        const index = keyPath.lastIndexOf('.') + 1; // Index at 1 (0 if missing)
1332        const key = keyPath.slice(index).replace(/:$/, ''); // Between "." & ":"
1333        const prefix = keyPath.slice(0, index - 1); // Get before first "."
1334        const target = index ? this.get(prefix, obj) : obj; // Drill down prefix
1335
1336        if (keyPath.endsWith(':')) { // If it's a dataProp style attribute
1337            const parentKey = val.substr(0, val.lastIndexOf('.'));
1338            val = this.get(val); // Resolve "val" from context, or JSON literal
1339            if (autoBind && !this.isJSON.test(val) && parentKey.includes('.')) {
1340                val = val.bind(this.get(parentKey)); // Parent is sub-obj, bind
1341            }
1342        }
1343        target[key] = val; // Assign the value to it's parent object
1344    }
1345});
1346
1347modulo.register('core', class DOMLoader {
1348    constructor(modulo) {
1349        this.modulo = modulo; // TODO: need to standardize back references to prevent mismatches
1350    }
1351
1352    getAllowedChildTags(parentName) {
1353        let tagsLower = this.modulo.config.domloader.topLevelTags; // "Modulo"
1354        if (/^_[a-z][a-zA-Z]+$/.test(parentName)) { // _likethis, e.g. _artifact
1355            tagsLower = [ parentName.toLowerCase().replace('_', '') ]; // Dead code?
1356        } else if (parentName) { // Normal parent, e.g. Library, Component etc
1357            const parentDef = this.modulo.definitions[parentName];
1358            const msg = `Invalid parent: ${ parentName } (${ parentDef })`;
1359            this.modulo.assert(parentDef && parentDef.Contains, msg);
1360            const names = Object.keys(this.modulo.registry[parentDef.Contains]);
1361            tagsLower = names.map(s => s.toLowerCase()); // Ignore case
1362        }
1363        return tagsLower;
1364    }
1365
1366    loadFromDOM(elem, parentName = null, quietErrors = false) {
1367        const { defaultDef } = this.modulo.config.modulo;
1368        const toCamel = s => s.replace(/-([a-z])/g, g => g[1].toUpperCase());
1369        const tagsLower = this.getAllowedChildTags(parentName);
1370        const array = [];
1371        for (const node of elem.children || []) {
1372            const partTypeLC = this.getDefType(node, tagsLower, quietErrors);
1373            if (node._moduloLoadedBy || partTypeLC === null) {
1374                continue; // Already loaded, or an ignorable or silenced error
1375            }
1376            node._moduloLoadedBy = this.modulo.id; // Mark as having loaded this
1377            // Valid definition, now create the "def" object
1378            const def = Object.assign({ Parent: parentName }, defaultDef);
1379            def.Content = node.tagName === 'SCRIPT' ? node.textContent : node.innerHTML;
1380            array.push(Object.assign(def, this.modulo.config[partTypeLC]));
1381            for (let name of node.getAttributeNames()) { // Loop through attrs
1382                const value = node.getAttribute(name);
1383                if (partTypeLC === name && !value) { // e.g. <def Script>
1384                    continue; // This is the "Type" attribute itself, skip
1385                }
1386                def[toCamel(name)] = value; // "-kebab-case" to "CamelCase"
1387            }
1388        }
1389        this.modulo.repeatProcessors(array, 'DefLoaders');
1390        return array;
1391    }
1392
1393    getDefType(node, tagsLower, quiet = false) {
1394        const { tagName, nodeType, textContent } = node;
1395        if (nodeType !== 1) { // Text nodes, comment nodes, etc
1396            if (nodeType === 3 && textContent && textContent.trim() && !quiet) {
1397                console.error(`Unexpected text in definition: ${textContent}`);
1398            }
1399            return null;
1400        }
1401        let defType = tagName.toLowerCase();
1402        if (defType in this.modulo.config.domloader.genericDefTags) {
1403            for (const attrUnknownCase of node.getAttributeNames()) {
1404                const attr = attrUnknownCase.toLowerCase();
1405                if (!node.getAttribute(attr) && tagsLower.includes(attr)) {
1406                    defType = attr; // Has an empty string value, is a def
1407                }
1408                break; // Always break: We will only look at first attribute
1409            }
1410        }
1411        if (!(tagsLower.includes(defType))) { // Were any discovered?
1412            if (defType === 'testsuite') { return null; } /* TODO Remove and add recipe to stub / silence TestSuite not found errors */
1413            if (!quiet) { // Invalid def / cPart: This type is not allowed here
1414                console.error(`"${ defType }" is not one of: ${ tagsLower }`);
1415            }
1416            return null // Return null to signify not a definition
1417        }
1418        return defType; // Valid, expected definition: Return lowercase type
1419    }
1420}, {
1421    topLevelTags: [ 'modulo' ], // Only "Modulo" is top
1422    genericDefTags: { def: 1, script: 1, template: 1, style: 1 },
1423});
1424
1425modulo.register('processor', function src (modulo, def, value) {
1426    const { getParentDefPath } = modulo.registry.utils;
1427    def.Source = (new window.URL(value, getParentDefPath(modulo, def))).href;
1428    modulo.fetchQueue.fetch(def.Source).then(text => {
1429        def.Content = (text || '') + (def.Content || '');
1430    });
1431});
1432
1433modulo.register('processor', function srcSync (modulo, def, value) {
1434    modulo.registry.processors.src(modulo, def, value);
1435    return true; // Only difference is return "true" for "wait" (TODO: Refactor to "return def.SrcSync ? false" then specify on Configuration)
1436});
1437
1438modulo.register('processor', function defTarget (modulo, def, value) {
1439    const resolverName = def.DefResolver || 'ValueResolver'; // TODO: document
1440    const resolver = new modulo.registry.core[resolverName](modulo);
1441    const target = value === null ? def : resolver.get(value); // Target object
1442    for (const [ key, defValue ] of Object.entries(def)) { // Resolve all values
1443        if (key.endsWith(':') || key.includes('.')) {
1444            delete def[key]; // Remove & replace unresolved value
1445            //resolver.set(/[^a-z]/.test(key) ? target : def, key, defValue); // TODO: Probably should be this -- not sure how this interacts with if
1446            resolver.set(/^[a-z]/.test(key) ? target : def, key, defValue);
1447        }
1448    }
1449});
1450
1451modulo.register('processor', function content (modulo, conf, value) {
1452    modulo.loadString(value, conf.DefinitionName);
1453});
1454
1455modulo.register('processor', function definedAs (modulo, def, value) {
1456    def.Name = value ? def[value] : (def.Name || def.Type.toLowerCase());
1457    const parentDef = modulo.definitions[def.Parent];
1458    const parentPrefix = parentDef && ('ChildPrefix' in parentDef) ?
1459        parentDef.ChildPrefix : (def.Parent ? def.Parent + '_' : '');
1460    def.DefinitionName = parentPrefix + def.Name;
1461    // Search for the next free Name by suffixing numbers
1462    while (def.DefinitionName in modulo.definitions) {
1463        const match = /([0-9]+)$/.exec(def.Name);
1464        const number = match ? match[0] : '';
1465        def.Name = def.Name.replace(number, '') + ((number * 1) + 1);
1466        def.DefinitionName = parentPrefix + def.Name;
1467    }
1468    modulo.definitions[def.DefinitionName] = def; // store definition
1469    const parentConf = modulo.definitions[def.Parent];
1470    if (parentConf) {
1471        parentConf.ChildrenNames = parentConf.ChildrenNames || [];
1472        parentConf.ChildrenNames.push(def.DefinitionName);
1473    }
1474});
1475
1476modulo.register('util', function initComponentClass (modulo, def, cls) {
1477    // Run factoryCallback static lifecycle method to create initRenderObj
1478    const initRenderObj = { elementClass: cls };
1479    for (const defName of def.ChildrenNames) {
1480        const cpartDef = modulo.definitions[defName];
1481        const cpartCls = modulo.registry.cparts[cpartDef.Type];
1482        if (cpartCls.factoryCallback) {
1483            const result = cpartCls.factoryCallback(initRenderObj, cpartDef, modulo);
1484            initRenderObj[cpartDef.RenderObj || cpartDef.Name] = result;
1485        }
1486    }
1487
1488    cls.prototype.init = function init () {
1489        this.modulo = modulo;
1490        this.isMounted = false;
1491        this.isModulo = true;
1492        this.originalHTML = null;
1493        this.originalChildren = [];
1494        this.cparts = modulo.instanceParts(def, { element: this });
1495    };
1496    modulo._connectedQueue = modulo._connectedQueue || []; // Ensure array
1497    modulo._drainQueue = () => { // "Clusters" all moduloMount calls
1498        while (modulo._connectedQueue.length > 0) { // Drains + invokes
1499            modulo._connectedQueue.shift().moduloMount();
1500        }
1501    };
1502    cls.prototype.connectedCallback = function connectedCallback () {
1503        modulo._connectedQueue.push(this);
1504        window.setTimeout(modulo._drainQueue, 0);
1505    };
1506    cls.prototype.moduloMount = function moduloMount(force = false) {
1507        if ((!this.isMounted && window.document.contains(this)) || force) {
1508            this.cparts.component._lifecycle([ 'initialized', 'mount', 'mountRender' ]);
1509        }
1510    };
1511    cls.prototype.initRenderObj = initRenderObj;
1512    cls.prototype.rerender = function (original = null) {
1513        if (!this.isMounted) { // Not mounted, do Mount which will also rerender
1514            return this.moduloMount();
1515        }
1516        this.cparts.component.rerender(original); // Otherwise, normal rerender
1517    };
1518    cls.prototype.getCurrentRenderObj = function () {
1519        return this.cparts.component.getCurrentRenderObj();
1520    };
1521    modulo.register('element', cls); // All elements get registered centrally
1522});
1523
1524modulo.register('util', function makeStore (modulo, def) {
1525    const isLower = key => key[0].toLowerCase() === key[0]; // skip "-prefixed"
1526    let data = modulo.registry.utils.keyFilter(def, isLower); // Get defaults
1527    data = JSON.parse(JSON.stringify(data)); // Deep copy to ensure primitives
1528    return { data, boundElements: {}, subscribers: [] };
1529});
1530
1531modulo.register('processor', function mainRequire (modulo, conf, value) {
1532    modulo.assets.mainRequire(value);
1533});
1534
1535modulo.config.artifact = {
1536    BuildCommandFinalizers: [ 'SaveArtifact' ],
1537    SaveArtifact: null,
1538    DefinedAs: 'name',
1539    exclude: '[modulo-asset]',
1540};
1541modulo.register('coreDef', class Artifact {
1542    static SaveArtifact (modulo, def, value) { // Build processor
1543        const artifactInstance = modulo.instance(def, { });
1544        artifactInstance.buildCommand(document); // e is document or target
1545        return true; // Always wait for next
1546    }
1547
1548    getBundle(targetElem) { // TODO: Mix in targetElem to QSA
1549        const bundledElems = [];
1550        for (const elem of targetElem.querySelectorAll(this.conf.bundle)) {
1551            const url = elem.getAttribute('src') || elem.getAttribute('href');
1552            if (this.conf.exclude && elem.matches(this.conf.exclude) || !url) {
1553                continue; // Need skip, otherwise it chokes assets or blank src
1554            }
1555            this.modulo.fetchQueue.fetch(url).then(text => { // Enqueue fetch
1556                delete this.modulo.fetchQueue.data[url]; // remove from cache
1557                elem.bundledContent = text; // attach back to element for later
1558            });
1559            bundledElems.push(elem);
1560        }
1561        return bundledElems;
1562    }
1563    getTemplateContext(targetElem) {
1564        const head = targetElem.head || { innerHTML: '' };
1565        const body = targetElem.body || { innerHTML: '', id: '' };
1566        const htmlPrefix = '<!DOCTYPE html><html><head>' + head.innerHTML;
1567        const htmlInterfix = '</head><body id="' + body.id + '">' + body.innerHTML;
1568        const htmlSuffix = '</body></html>';
1569        const bundle = this.conf.bundle ? this.getBundle(targetElem) : null;
1570        const extras = { htmlPrefix, htmlInterfix, htmlSuffix, bundle };
1571        return Object.assign({ }, this.modulo, extras);
1572    }
1573
1574    buildCommand(targetElem) {
1575        const { Template } = this.modulo.registry.cparts;
1576        const { saveFileAs, hash } = this.modulo.registry.utils;
1577        const { DefinitionName, remove, urlName, name, Content } = this.conf;
1578        const def = this.modulo.definitions[DefinitionName]; // Get original def
1579        if (remove) { // Need to remove elements from document first
1580            targetElem.querySelectorAll(remove).forEach(elem => elem.remove());
1581        }
1582        this.templateContext = this.getTemplateContext(targetElem); // Queue up
1583        this.modulo.fetchQueue.enqueue(() => { // Drain queue before continue
1584            const tmplt = new Template(Content); // Render file Artifact content
1585            const content = tmplt.render(this.templateContext);
1586            def.FileName = `modulo-build-${ hash(content) }.${ name }`;
1587            if (urlName) { // Guess filename based on URL (or use as default)
1588                def.FileName = window.location.pathname.split('/').pop() || urlName;
1589            }
1590            def.OutputPath = saveFileAs(def.FileName, content); // Attempt save
1591        });
1592    }
1593});
1594
1595modulo._DEVLIB_SOURCE = (`
1596<Artifact name="css" bundle="link[rel=stylesheet]">
1597    {% for elem in bundle %}{{ elem.bundledContent|default:''|safe }}
1598    {% endfor %}{% for css in assets.cssAssetsArray %}
1599    {{ css|safe }}{% endfor %}
1600</Artifact>
1601<Artifact name="js" bundle="script[src]">
1602    window.moduloBuild = window.moduloBuild || { modules: {}, nameToHash: {} };
1603    {% for name, hash in assets.nameToHash %}{% if hash in assets.moduleSources %}{% if name|first is not "_" %}
1604        window.moduloBuild.modules["{{ hash }}"] = function {{ name }} (modulo) {
1605            {{ assets.moduleSources|get:hash|safe }}
1606        };
1607        window.moduloBuild.nameToHash.{{ name }} = "{{ hash }}";
1608    {% endif %}{% endif %}{% endfor %}
1609    window.moduloBuild.definitions = { {% for name, value in definitions %}
1610        {% if name|first is not "_" %}{{ name }}: {{ value|json|safe }},{% endif %}
1611    {% endfor %} };
1612    {% if bundle %}
1613        {% for elem in bundle %}{{ elem.bundledContent|default:''|safe }}{% endfor %}
1614        modulo.assets.modules = window.moduloBuild.modules;
1615        modulo.assets.nameToHash = window.moduloBuild.nameToHash;
1616        modulo.definitions = window.moduloBuild.definitions;
1617    {% endif %}
1618    {% for name in assets.mainRequires %}
1619        modulo.assets.require("{{ name|escapejs }}");
1620    {% endfor %}
1621</Artifact>
1622<Artifact name="html" url-name="index.html" remove="script[src],link[href],[modulo-asset],template[modulo],script[modulo],modulo">
1623    {{ htmlPrefix|safe }}<link rel="stylesheet" href="{{ definitions._artifact_css.OutputPath }}" />
1624    {{ htmlInterfix|safe }}<script src="{{ definitions._artifact_js.OutputPath }}"></s` + `cript>
1625    {{ htmlSuffix|safe }}
1626</Artifact>
1627`).replace(/^\s+/gm, ''); // Removing indentation from DEVLIB_SOURCE
1628
1629modulo.config.component = {
1630    tagAliases: { }, //tagAliases: { 'html-table': 'table', 'html-script': 'script' }, // shortcut for syntax issues
1631    mode: 'regular',
1632    rerender: 'event',
1633    engine: 'Reconciler', // TODO: RM, dead code
1634    Contains: 'cparts',
1635    CustomElement: 'window.HTMLElement',
1636    DefinedAs: 'name',
1637    BuildLifecycle: 'build',
1638    RenderObj: 'component', // Make features available as "renderObj.component" 
1639    // Children: 'cparts', // How we can implement Parentage: Object.keys((get('modulo.registry.' + value))// cparts))
1640    DefLoaders: [ 'DefTarget', 'DefinedAs', 'Src', 'Content' ],
1641    DefBuilders: [ 'CustomElement', 'alias|AliasNamespace', 'CodeTemplate' ],
1642    DefFinalizers: [ 'MainRequire' ],
1643    BuildCommandBuilders: [ 'Prebuild|BuildLifecycle', 'BuildLifecycle' ],
1644    //Directives: [ 'slotLoad', 'eventMount', 'eventUnmount', 'dataPropMount', 'dataPropUnmount' ],
1645    Directives: [ 'eventMount', 'eventUnmount', 'dataPropMount', 'dataPropUnmount' ],
1646    CodeTemplate: `
1647        const def = modulo.definitions['{{ def.DefinitionName }}'];
1648        class {{ def.className }} extends {{ def.baseClass|default:'window.HTMLElement' }} {
1649            constructor() { super(); this.init(); }
1650        }
1651        modulo.registry.utils.initComponentClass(modulo, def, {{ def.className }});
1652        window.customElements.define(def.TagName, {{ def.className }});
1653        return {{ def.className }};
1654    `.replace(/^\s+/gm, ''),
1655};
1656
1657modulo.register('coreDef', class Component {
1658    static CustomElement (modulo, def, value) {
1659        if (!def.ChildrenNames || def.ChildrenNames.length === 0) {
1660            console.warn('MODULO: Empty ChildrenNames:', def.DefinitionName);
1661            return;
1662        } else if (def.namespace === null || def.alias) { // Auto-gen
1663            def.namespace = def.namespace || def.DefinitionName;
1664        } else if (!def.namespace) { // Otherwise default to the Modulo def conf
1665            def.namespace = modulo.config.namespace || 'x'; // or simply 'x-'
1666        }
1667        def.name = def.name || def.DefName || def.Name;
1668        def.TagName = `${ def.namespace }-${ def.name }`.toLowerCase();
1669        def.MainRequire = def.DefinitionName;
1670        def.className =  def.className || `${ def.namespace }_${ def.name }`;
1671    }
1672
1673    static BuildLifecycle (modulo, def, value) {
1674        for (const elem of document.querySelectorAll(def.TagName)) {
1675            elem.cparts.component._lifecycle([ value ]); // Run the lifecycle
1676        }
1677        return true;
1678    }
1679
1680    static AliasNamespace (modulo, def, value) {
1681        const fullAlias = `${ value }-${ def.name }`; // Combine new NS and name
1682        modulo.config.component.tagAliases[fullAlias] = def.TagName;
1683    }
1684
1685    rerender(original = null) {
1686        if (original) {
1687            if (this.element.originalHTML === null) {
1688                this.element.originalHTML = original.innerHTML;
1689            }
1690            this.element.originalChildren = Array.from(
1691                original.hasChildNodes() ? original.childNodes : []);
1692        }
1693        this._lifecycle([ 'prepare', 'render', 'dom', 'reconcile', 'update' ]);
1694    }
1695
1696    buildCallback() {
1697        const PRE = 'modulo-mount-'; // Prefix used for attributes
1698        if (this.element.originalHTML !== this.element.innerHTML) {
1699            this.element.setAttribute(PRE + 'html', this.element.originalHTML);
1700        }
1701        const nodes = Array.from(this.element.querySelectorAll('*'));
1702        for (const [ node, method, arg ] of this._mountPatchset || []) {
1703            const { rawName, el } = arg || {}; // Extract needed directive info
1704            const count = el ? nodes.filter(e => e.contains(el)).length : 0;
1705            if (count) { // count = number of steps in tree hierarchy (or 0)
1706                const existing = el.getAttribute(PRE + 'patches') || '';
1707                if (!existing.includes(count + ',' + rawName)) { // Not a dupe
1708                    const value = existing + '\n' + count + ',' + rawName;
1709                    el.setAttribute(PRE + 'patches', value.trim());
1710                }
1711            }
1712        }
1713    }
1714
1715    getCurrentRenderObj() {
1716        return (this.element.eventRenderObj || this.element.renderObj || this.element.initRenderObj);
1717    }
1718
1719    _lifecycle(lifecycleNames, rObj={}) {
1720        const renderObj = Object.assign({}, rObj, this.getCurrentRenderObj());
1721        this.element.renderObj = renderObj;
1722        this.modulo.lifecycle(this.element.cparts, renderObj, lifecycleNames);
1723        //this.element.renderObj = null; // ?rendering is over, set to null
1724    }
1725
1726    initializedCallback() {
1727        const { newNode } = this.modulo.registry.utils;
1728        const opts = { directiveShortcuts: [], directives: [] }; // TODO: Move this to reconciler
1729        for (const cPart of Object.values(this.element.cparts)) {
1730            const def = (cPart.def || cPart.conf);
1731            for (const method of def.Directives || []) {
1732                const dirName = (def.RenderObj || def.Name) + '.' + method;
1733                opts.directives[dirName] = cPart;
1734            }
1735        }
1736        this.reconciler = new this.modulo.registry.core.Reconciler(this.modulo, opts);
1737        this.resolver = new this.modulo.registry.core.ValueResolver(this.modulo);
1738        const html = this.element.getAttribute('modulo-mount-html'); // Hydrate?
1739        this._mountRival = html === null ? this.element : newNode(html);
1740        this.element.originalHTML = html === null ? this.element.innerHTML : html;
1741    }
1742
1743    mountCallback() { // Prepare the element, "hydrating" the "mount-patches"
1744        const ATTR = 'modulo-mount-patches'; // Attribute used
1745        const { get } = this.modulo.registry.utils;
1746        for (const elem of this.element.querySelectorAll(`[${ ATTR }]`)) {
1747            for (const line of elem.getAttribute(ATTR).split('\n')) {
1748                const [ count, rawName ] = line.split(','); // Comma seperated
1749                const nodePath = '.parentNode'.repeat(count).substr(1);
1750                if (this.element === get(elem, nodePath)) { // It's me!
1751                    this.reconciler.patchDirectives(elem, rawName, 'Mount');
1752                    const newVal = elem.getAttribute(ATTR).replace(line, '');
1753                    elem.setAttribute(ATTR, newVal); // "Consume" line from attr
1754                }
1755            }
1756        }
1757    }
1758
1759    mountRenderCallback() { // First "mount", trigger render & hydration
1760        this.reconciler.applyPatches(this.reconciler.patches); // From "mount"
1761        this.rerender(this._mountRival); // render + mount childNodes
1762        delete this._mountRival; // Clear the temporary reference
1763        this.element.isMounted = true; // Mark as mounted
1764    }
1765
1766    prepareCallback() {
1767        return { // Create the initial Component renderObj obj
1768            originalHTML: this.element.originalHTML, // HTML received at mount
1769            id: this.id, // Universally unique ID number
1770            innerHTML: null, // String to copy (default: null is "no-op")
1771            innerDOM: null, // Node to copy (default: null sets innerHTML)
1772            patches: null, // Patch array (default: reconcile vs innerDOM)
1773            slots: { }, // Populate with slots to be filled when reconciling
1774        };
1775    }
1776
1777    domCallback(renderObj) {
1778        const { clone, newNode } = this.modulo.registry.utils;
1779        let { slots, root, innerHTML, innerDOM } = renderObj.component;
1780        if (this.attrs.mode === 'regular' || this.attrs.mode === 'vanish') {
1781            root = this.element; // default, use element as root
1782        } else if (this.attrs.mode === 'shadow') {
1783            if (!this.element.shadowRoot) {
1784                this.element.attachShadow({ mode: 'open' });
1785            }
1786            root = this.element.shadowRoot; // render into attached shadow
1787        } else if (this.attrs.mode === 'vanish-into-document') {
1788            root = this.element.ownerDocument.body; // render into body
1789        } else if (!root) {
1790            this.modulo.assert(this.attrs.mode === 'custom-root', 'Bad mode')
1791        }
1792        if (innerHTML !== null && !innerDOM) { // Use component.innerHTML as DOM
1793            innerDOM = newNode(innerHTML);
1794        }
1795        if (innerDOM && this.attrs.mode === 'vanish-into-document') {
1796            for (const el of innerDOM.querySelectorAll('script,link,title,meta')) {
1797                this.element.ownerDocument.head.append(clone(el)); // Move clone
1798                el.remove(); // Old one is removed from previous location
1799            }
1800        }
1801        if (innerDOM && this.attrs.mode !== 'shadow') {
1802            for (const elem of this.element.originalChildren) {
1803                const name = (elem.getAttribute && elem.getAttribute('slot')) || '';
1804                elem.remove(); // Remove from DOM so it can't self-match
1805                if (!(name in slots)) {
1806                    slots[name] = [ elem ]; // Sorting into new slot arrays
1807                } else {
1808                    slots[name].push(elem); // Or pushing into existing
1809                }
1810            }
1811        }
1812        return { root, innerHTML, innerDOM, slots };
1813    }
1814
1815    reconcileCallback(renderObj) {
1816        let { innerHTML, innerDOM, patches, root, slots } = renderObj.component;
1817        if (innerDOM) {
1818            this.reconciler.patches = []; // Reset reconciler patches
1819            this.reconciler.reconcileChildren(root, innerDOM, slots);
1820            patches = this.reconciler.patches;
1821        }
1822        return { patches, innerHTML }; // TODO remove innerHTML from here
1823    }
1824
1825    updateCallback(renderObj) {
1826        const { patches } = renderObj.component;
1827        if (patches) {
1828            this._mountPatchset = this._mountPatchset || patches; // 1st render
1829            this.reconciler.applyPatches(patches); // Apply patches to DOM
1830        }
1831        if (this.attrs.mode === 'vanish') { // If we need to vanish, do it now
1832            this.element.replaceWith(...this.element.childNodes);
1833        }
1834    }
1835
1836    handleEvent(func, payload, ev) {
1837        this._lifecycle([ 'event' ]);
1838        func(payload === undefined ? ev : payload);
1839        this._lifecycle([ 'eventCleanup' ]);
1840        if (this.attrs.rerender !== 'manual') { // TODO: Change patch('rerender') to "requestRerender" (or update the HTMLElement method)
1841            this.element.rerender(); // always rerender after events
1842        }
1843    }
1844
1845    eventMount({ el, value, attrName, rawName }) {
1846        const { resolveDataProp } = this.modulo.registry.utils;
1847        const get = (key, key2) => resolveDataProp(key, el, key2 && get(key2));
1848        this.modulo.assert(get(attrName), `Not found: ${ rawName }=${ value }`);
1849        el.moduloEvents = el.moduloEvents || {}; // Attach if not already
1850        const listen = ev => { // Define a listen event func to run handleEvent
1851            ev.preventDefault();
1852            const payload = get(attrName + '.payload', 'payload');
1853            this.handleEvent(resolveDataProp(attrName, el), payload, ev);
1854        };
1855        el.moduloEvents[attrName] = listen;
1856        el.addEventListener(attrName, listen);
1857    }
1858
1859    eventUnmount({ el, attrName }) {
1860        if (el.moduloEvents) { // TODO: Remove this check
1861            el.removeEventListener(attrName, el.moduloEvents[attrName]);
1862            delete el.moduloEvents[attrName];
1863        }
1864    }
1865
1866    dataPropMount({ el, value, attrName, rawName }) { // element, 
1867        // Resolve the given value and attach to dataProps
1868        if (!el.dataProps) {
1869            el.dataProps = {};
1870            el.dataPropsAttributeNames = {};
1871        }
1872        const resolver = new this.modulo.registry.core.ValueResolver(// OLD TODO: Global modulo
1873                      this.element && this.element.getCurrentRenderObj());
1874        resolver.set(el.dataProps, attrName + ':', value, true);
1875        el.dataPropsAttributeNames[rawName] = attrName;
1876    }
1877
1878    dataPropUnmount({ el, attrName, rawName }) {
1879        if (!el.dataProps) { console.log('Modulo ERROR: Could not Unmount', attrName, rawName, el); }
1880        if (el.dataProps) {
1881            delete el.dataProps[attrName];
1882            delete el.dataPropsAttributeNames[rawName];
1883        }
1884    }
1885});
1886
1887modulo.register('coreDef', class Library { }, {
1888    Contains: 'coreDefs',
1889    DefTarget: 'config.component',
1890    DefLoaders: [ 'DefTarget', 'DefinedAs', 'Src', 'Content' ],
1891});
1892
1893modulo.register('util', function keyFilter (obj, func) {
1894    const keys = func.call ? Object.keys(obj).filter(func) : func;
1895    return Object.fromEntries(keys.map(key => [ key, obj[key] ]));
1896});
1897
1898modulo.register('util', function resolveDataProp (key, elem, defaultVal) {
1899    if (elem.dataProps && key in elem.dataProps) {
1900        return elem.dataProps[key];
1901    }
1902    return elem.hasAttribute(key) ? elem.getAttribute(key) : defaultVal;
1903});
1904
1905modulo.register('util', function cleanWord (text) {
1906    // todo: should merge with stripWord ? See if "strip" functionality is enough
1907    return (text + '').replace(/[^a-zA-Z0-9$_\.]/g, '') || '';
1908});
1909
1910modulo.register('util', function stripWord (text) {
1911    return text.replace(/^[^a-zA-Z0-9$_\.]/, '')
1912               .replace(/[^a-zA-Z0-9$_\.]$/, '');
1913});
1914
1915modulo.register('util', function clone (el) { // Clones an element
1916    const newElement = window.document.createElement(el.tagName);
1917    for (const attr of el.attributes) { // Copy all attributes from old elem
1918        newElement.setAttributeNode(attr.cloneNode(true)); // ...to new elem
1919    }
1920    newElement.textContent = el.textContent; // (e.g. <title>Hi</title>)
1921    if (el.tagName === 'SCRIPT') { // Need to fix SCRIPT tag fluke
1922        newElement.src = el.src; // (TODO: Still needed?)
1923    }
1924    return newElement;
1925});
1926
1927modulo.register('util', function hash (str) {
1928    // Simple, insecure, "hashCode()" implementation. Returns base32 hash
1929    let h = 0;
1930    for (let i = 0; i < str.length; i++) {
1931        //h = ((h << 5 - h) + str.charCodeAt(i)) | 0;
1932        h = Math.imul(31, h) + str.charCodeAt(i) | 0;
1933    }
1934    const hash8 = ('---------' + (h || 0).toString(32)).slice(-8);
1935    return hash8.replace(/-/g, 'x'); // Pad with 'x'
1936});
1937
1938modulo.register('util', function newNode(innerHTML, tag) {
1939    const obj = { innerHTML }; // Extra properties to assign
1940    return Object.assign(window.document.createElement(tag || 'div'), obj);
1941});
1942modulo.registry.utils.makeDiv = modulo.registry.utils.newNode; // TODO: Rm alias
1943
1944modulo.register('util', function normalize(html) {
1945    // Normalize space to ' ' & trim around tags
1946    return html.replace(/\s+/g, ' ').replace(/(^|>)\s*(<|$)/g, '$1$2').trim();
1947});
1948
1949modulo.register('util', function escapeRegExp(s) {
1950    return s.replace(/[.*+?^${}()|[\]\\]/g, "\\" + "\x24" + "&");
1951});
1952
1953modulo.register('util', function saveFileAs(filename, text) {
1954    const element = window.document.createElement('a');
1955    const enc = window.encodeURIComponent(text);
1956    element.setAttribute('href', 'data:text/plain;charset=utf-8,' + enc);
1957    element.setAttribute('download', filename);
1958    window.document.body.appendChild(element);
1959    element.click();
1960    window.document.body.removeChild(element);
1961    return `./${filename}`; // by default, return local path
1962});
1963
1964modulo.register('util', function get(obj, key) {
1965    return (key in obj) ? obj[key] : (key + '').split('.').reduce((o, name) => o[name], obj);
1966});
1967
1968modulo.register('util', function set(obj, keyPath, val) {
1969    return new window.modulo.registry.core.ValueResolver(window.modulo).set(obj, keyPath, val);
1970});
1971
1972modulo.register('util', function getParentDefPath(modulo, def) {
1973    const { getParentDefPath } = modulo.registry.utils; // Use to recurse
1974    const pDef = def.Parent ? modulo.definitions[def.Parent] : null;
1975    const url = String(window.location).split('?')[0]; // Remove ? and #
1976    return pDef ? pDef.Source || getParentDefPath(modulo, pDef) : url;
1977});
1978
1979modulo.register('core', class AssetManager {
1980    constructor (modulo) { // TODO: Refactor this class into util / processors
1981        this.modulo = modulo;
1982        this.stylesheets = {}; // Object with hash of CSS (prevents double add)
1983        this.cssAssetsArray = []; // List of CSS assets added, in order
1984        this.modules = {}; // Object containing JS functions with hashed keys
1985        this.moduleSources = {}; // Source code of JS functions (for build)
1986        this.nameToHash = {}; // Reversable hash / human name for modules
1987        this.mainRequires = []; // List of globally invoked modules
1988    }
1989
1990    mainRequire(moduleName) {
1991        this.mainRequires.push(moduleName);
1992        this.require(moduleName);
1993    }
1994
1995    require(moduleName) {
1996        // TODO: Don't use nameToHash for simpler look-up, but include
1997        // "hashToName" for deduping during add (just create extra refs)
1998        this.modulo.assert(moduleName in this.nameToHash,
1999            `${ moduleName } not in ${ Object.keys(this.nameToHash).join(', ') }`);
2000        const hash = this.nameToHash[moduleName];
2001        this.modulo.assert(hash in this.modules,
2002            `${ moduleName } / ${ hash } not in ${ Object.keys(this.modules).join(', ') }`);
2003        return this.modules[hash].call(window, this.modulo);
2004    }
2005
2006    define(name, code) {
2007        const hash = this.modulo.registry.utils.hash(code);
2008        this.modulo.assert(!(name in this.nameToHash), `Duplicate: ${ name }`);
2009        this.nameToHash[name] = hash;
2010        if (!(hash in this.modules)) {
2011            this.moduleSources[hash] = code;
2012            const assignee = `window.modulo.assets.modules["${ hash }"] = `;
2013            const prefix = assignee + `function ${ name }
2013 (modulo) {\n`;
2014            this.appendToHead('script', `"use strict";${ prefix }${ code }};\n`);
2015        }
2016    }
2017
2018    appendToHead(tagName, codeStr) {
2019        const doc = window.document;
2020        if (!doc || !doc.head) { // Fall back to just using a Function
2021            return new Function('window', 'modulo', codeStr)(window, this.modulo);
2022        }
2023        const elem = doc.createElement(tagName);
2024        elem.setAttribute('modulo-asset', 'y'); // Mark as an "asset"
2025        doc.head.append(elem);
2026        elem.textContent = codeStr; // Blocking, causes eval
2027    }
2028});
2029
2030modulo.register('core', class FetchQueue {
2031    constructor(modulo, queue = {}, data = {}) {
2032        Object.assign(this, { modulo, queue, data });
2033        this.wait = callback => this.enqueue(callback, true); // TODO: RM this alias
2034    }
2035
2036    fetch(src) {  // Returns "thennable" that somewhat resembles window.fetch
2037        return { then: callback => this.request(src, callback, console.error) };
2038    }
2039
2040    request(src, resolve, reject) { // Do fetch & do enqueue
2041        if (src in this.data) { // Cached data found
2042            resolve(this.data[src], src); // (sync route)
2043        } else if (!(src in this.queue)) { // No cache, no queue
2044            this.queue[src] = [ resolve ]; // First time, create the queue Array
2045            const { force, callbackName } = this.modulo.config.fetchqueue;
2046            if ((!force && src.startsWith('file:/')) || force === 'file') {
2047                window[callbackName] = str => { this.__data = str };
2048                const elem = window.document.createElement('SCRIPT');
2049                elem.onload = () => this.receiveData(this.__data, src);
2050                elem.src = src + (src.endsWith('/') ? 'index.html' : '');
2051                elem.setAttribute('modulo-asset', 'y'); // Stay out of build
2052                window.document.head.append(elem); // Actually execute request
2053            } else { // Otherwise, use normal fetch transport method
2054                window.fetch(src, { cache: 'no-store' })
2055                    .then(response => response.text())
2056                    .then(text => this.receiveData(text, src))
2057                    .catch(reject);
2058            }
2059        } else { // Otherwise: Already requested, only enqueue function
2060            this.queue[src].push(resolve);
2061        }
2062    }
2063
2064    receiveData(text, src) { // Receive data, optionally trimming padding
2065        const { prefix, suffix } = this.modulo.config.fetchqueue.filePadding;
2066        if (text && text.startsWith(prefix) && prefix && text.trim().endsWith(suffix)) {
2067            text = text.trim().slice(prefix.length, 0 - suffix.length); // Clean
2068        }
2069        this.data[src] = text; // Keep retrieved data cached here for sync route
2070        const resolveCallbacks = this.queue[src];
2071        delete this.queue[src];
2072        for (const dataCallback of resolveCallbacks) { // Loop through callbacks
2073            dataCallback(text, src);
2074        }
2075    }
2076
2077    enqueue(callback, waitForAll = false) { // Wait for _current_ queue (or all)
2078        const allQueues = Array.from(Object.values(this.queue)); // Copy array
2079        if (allQueues.length === 0) {
2080            return callback();
2081        } else if (waitForAll) { // Doing a "wait()" -- need to re-enqueue
2082            return this.enqueue(() => Object.keys(this.queue).length === 0 ?
2083                                      callback() : this.enqueue(callback, true));
2084        }
2085        let count = 0; // Using count we only do callback() when ALL returned
2086        const check = () => ((++count >= allQueues.length) ? callback() : 0);
2087        allQueues.forEach(queue => queue.push(check)); // Add to every queue
2088    }
2089}, {
2090    callbackName: 'DOCTYPE_MODULO',
2091    filePadding: { prefix: '!DOCTYPE_MODULO(`', suffix: '`)' },
2092});
2093
2094modulo.register('cpart', class Props {
2095    initializedCallback(renderObj) {
2096        const props = {};
2097        const { resolveDataProp } = this.modulo.registry.utils;
2098        for (const [ propName, def ] of Object.entries(this.attrs)) {
2099            props[propName] = resolveDataProp(propName, this.element, def);
2100            // TODO: Implement type-checked, and required
2101        }
2102        return props;
2103    }
2104
2105    prepareCallback(renderObj) {
2106        /* TODO: Remove after observedAttributes is implemented, e.g.:
2107          static factoryCallback({ attrs }, { componentClass }, renderObj) {
2108              //componentClass.observedAttributes = Object.keys(attrs);
2109          }
2110        */
2111        return this.initializedCallback(renderObj);
2112    }
2113});
2114
2115modulo.config.style = {
2116    AutoIsolate: true, // true is "default behavior" (autodetect)
2117    urlReplace: null, // null is "default behavior" (only if -src is specified)
2118    isolateSelector: null, // Later has list of selectors
2119    isolateClass: null, // By default, it does not use class isolation
2120    prefix: null, // Used to specify prefix-based isolation (most common)
2121    corePseudo: ['before', 'after', 'first-line', 'last-line' ],
2122    DefBuilders: [ 'FilterContent', 'AutoIsolate', 'Content|ProcessCSS' ],
2123};
2124modulo.register('cpart', class Style {
2125    static AutoIsolate(modulo, def, value) {
2126        const { AutoIsolate } = modulo.registry.cparts.Style; // (for recursion)
2127        const { namespace, mode, Name } = modulo.definitions[def.Parent] || {};
2128        if (value === true) { // Auto-detect based on parent's mode
2129            AutoIsolate(modulo, def, mode); // Check "mode" instead (1x recurse)
2130        } else if (value === 'regular' && !def.isolateClass) { // Use prefix
2131            def.prefix = def.prefix || `${ namespace }-${ Name }`;
2132        } else if (value === 'vanish') { // Vanish-based, specify "isolateClass"
2133            def.isolateClass = def.isolateClass || def.Parent;
2134        }
2135    }
2136    static processSelector (modulo, def, selector) {
2137        const hostPrefix = def.prefix || ('.' + def.isolateClass);
2138        if (def.isolateClass || def.prefix) {
2139            // Upgrade the ":host" or :root pseudo-elements to be the full name
2140            const hostRegExp = new RegExp(/:(host|root)(\([^)]*\))?/, 'g');
2141            selector = selector.replace(hostRegExp, hostClause => {
2142                hostClause = hostClause.replace(/:(host|root)/gi, '');
2143                return hostPrefix + (hostClause ? `:is(${ hostClause })` : '');
2144            });
2145        }
2146        let selectorOnly = selector.replace(/\s*[\{,]\s*,?$/, '').trim();
2147        if (def.isolateClass && selectorOnly !== hostPrefix) {
2148            // Remove extraneous characters (and strip ',' for isolateSelector)
2149            let suffix = /{\s*$/.test(selector) ? ' {' : ', ';
2150            selectorOnly = selectorOnly.replace(/:(:?[a-z-]+)\s*$/i, (all, pseudo) => {
2151                if (pseudo.startsWith(':') || def.corePseudo.includes(pseudo)) {
2152                    suffix = ':' + pseudo + suffix; // Attach to suffix, on outside
2153                    return ''; // Strip pseudo from the selectorOnly variable
2154                }
2155                return all;
2156            });
2157            def.isolateSelector.push(selectorOnly); // Add to array for later
2158            selector = `.${ def.isolateClass }:is(${ selectorOnly })` + suffix;
2159        }
2160        if (def.prefix && !selector.startsWith(def.prefix)) {
2161            // A prefix was specified, so prepend it if it doesn't have it
2162            selector = `${ def.prefix } ${ selector }`;
2163        }
2164        return selector;
2165    }
2166    static ProcessCSS (modulo, def, value) {
2167        if (def.isolateClass || def.prefix) {
2168            if (!def.keepComments) {
2169                value = value.replace(/\/\*.+?\*\//g, ''); // strip comments
2170            }
2171            def.isolateSelector = []; // Used to accumulate elements to select
2172            value = value.replace(/([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/gi, selector => {
2173                selector = selector.trim();
2174                if (selector.startsWith('@') || selector.startsWith('from')
2175                                              || selector.startsWith('to')) {
2176                    return selector; // Skip (e.g. is @media or @keyframes)
2177                }
2178                return this.processSelector(modulo, def, selector);
2179            });
2180        }
2181        if (def.urlReplace || (def.urlReplace === null && def.Source)) {
2182            const key = def.urlReplace === 'absolute' ? 'href' : 'pathname';
2183            value = value.replace(/url\(['"]?([^)]+?)['"]?\)/gi, (all, url) => {
2184                if (url.startsWith('.')) { // If relative, make absolute
2185                    return `url("${ (new window.URL(url, def.Source))[key] }")`;
2186                }
2187                return all; // Not a relative URL, return all text untampered
2188            });
2189        }
2190        const { mode } = modulo.definitions[def.Parent] || {};
2191        if (mode === 'shadow') { // Stash in the definition configuration
2192            def.shadowContent = (def.shadowContent || '') + value;
2193        } else { // Otherwise, just "register" as a modulo asset
2194            def.headHash = modulo.registry.utils.hash(value);
2195            if (!(def.headHash in modulo.assets.stylesheets)) {
2196                modulo.assets.stylesheets[def.headHash] = value;
2197                modulo.assets.cssAssetsArray.push(value);
2198            }
2199        }
2200    }
2201    makeStyleTag(parentElem, content) { // Append <style> tag with content
2202        const style = window.document.createElement('style');
2203        style.setAttribute('modulo-asset', 'y');
2204        style.textContent = content;
2205        parentElem.append(style);
2206    }
2207    domCallback(renderObj) {
2208        const { mode } = modulo.definitions[this.conf.Parent] || {};
2209        const { innerDOM, Parent } = renderObj.component;
2210        const { headHash, isolateClass, isolateSelector, shadowContent } = this.conf;
2211        if (isolateClass && isolateSelector && innerDOM) { // Attach classes
2212            const selector = isolateSelector.filter(s => s).join(',\n');
2213            for (const elem of innerDOM.querySelectorAll(selector)){
2214                elem.classList.add(isolateClass);
2215            }
2216        }
2217        if (shadowContent && innerDOM) { // Append to element to reconcile
2218            this.makeStyleTag(innerDOM, shadowContent);
2219        }
2220        const devStyles = this.modulo.assets.stylesheets;
2221        if (headHash && headHash in devStyles) { // Add to page: Hasn't been yet
2222            this.makeStyleTag(window.document.head, devStyles[headHash]);
2223            delete this.conf.headHash; // Consume, so it only happens once
2224        }
2225    }
2226});
2227
2228modulo.register('cpart', class Template {
2229    static TemplatePrebuild (modulo, def, value) {
2230        modulo.assert(def.Content, `Empty Template: ${def.DefinitionName}`);
2231        const template = modulo.instance(def, { });
2232        // def.Content = def.Content.trim(); // XXX TODO BREAKS IA ALL?
2233        const compiledCode = template.compileFunc(def.Content);
2234        const code = `return function (CTX, G) { ${ compiledCode } };`;
2235        // TODO: Refactor this to use define processor?
2236        modulo.assets.define(def.DefinitionName, code);
2237        delete def.Content;
2238    }
2239    constructor(text, options = null) {
2240        if (typeof text === 'string') { // Using "new" (direct JS interface)
2241            window.modulo.instance({ }, options || { }, this); // Setup object
2242            this.conf.DefinitionName = '_template_template' + this.id; // Unique
2243            const code = `return function (CTX, G) { ${ this.compileFunc(text) } };`;
2244            this.modulo.assets.define(this.conf.DefinitionName, code);
2245        }
2246    }
2247
2248    constructedCallback() {
2249        this.stack = []; // Parsing tag stack, used to detect unclosed tags
2250        // Combine conf from all sources: config, defaults, and "registered"
2251        const { filters, tags } = this.conf;
2252        const { defaultFilters, defaultTags } = this.modulo.config.template;
2253        const { templateFilters, templateTags } = this.modulo.registry;
2254        Object.assign(this, this.modulo.config.template, this.conf);
2255        // Set "filters" and "tags" with combined / squashed configuration
2256        this.filters = Object.assign({}, defaultFilters, templateFilters, filters);
2257        this.tags = Object.assign({}, defaultTags, templateTags, tags);
2258    }
2259
2260    initializedCallback() {
2261        return { render: this.render.bind(this) }; // Export "render" method
2262    }
2263
2264    renderCallback(renderObj) {
2265        if (this.conf.Name === 'template' || this.conf.active) { // If primary
2266            renderObj.component.innerHTML = this.render(renderObj); // Do render
2267        }
2268    }
2269
2270    parseExpr(text) {
2271        // Output JS code that evaluates an equivalent template code expression
2272        const filters = text.split('|');
2273        let results = this.parseVal(filters.shift()); // Get left-most val
2274        for (const [ fName, arg ] of filters.map(s => s.trim().split(':'))) {
2275            // TODO: Store a list of variables / paths, so there can be
2276            // warnings or errors when variables are unspecified
2277            const argList = arg ? ',' + this.parseVal(arg) : '';
2278            results = `G.filters["${fName}"](${results}${argList})`;
2279        }
2280        return results;
2281    }
2282
2283    parseCondExpr(string) {
2284        // Return an Array that splits around ops in an "if"-style statement
2285        const regExpText = ` (${this.opTokens.split(',').join('|')}) `;
2286        return string.split(RegExp(regExpText));
2287    }
2288
2289    toCamel(string) { // Takes kebab-case and converts toCamelCase
2290        return string.replace(/-([a-z])/g, g => g[1].toUpperCase());
2291    }
2292
2293    parseVal(string) {
2294        // Parses str literals, de-escaping as needed, numbers, and context vars
2295        const { cleanWord } = modulo.registry.utils;
2295 // TODO: RM this "safety"
2296        const s = string.trim();
2297        if (s.match(/^('.*'|".*")$/)) { // String literal
2298            return JSON.stringify(s.substr(1, s.length - 2));
2299        }
2300        return s.match(/^\d+$/) ? s : `CTX.${ cleanWord(this.toCamel(s)) }`
2301    }
2302
2303    escapeText(text) {
2304        if (text && text.safe) {
2305            return text;
2306        }
2307        return (text + '').replace(/&/g, '&amp;')
2308            .replace(/</g, '&lt;').replace(/>/g, '&gt;')
2309            .replace(/'/g, '&#x27;').replace(/"/g, '&quot;');
2310    }
2311
2312    tokenizeText(text) {
2313        // Join all modeTokens with | (OR in regex)
2314        const { escapeRegExp } = this.modulo.registry.utils;
2315        const re = '(' + this.modeTokens.map(escapeRegExp).join('|(').replace(/ +/g, ')(.+?)');
2316        return text.split(RegExp(re)).filter(token => token !== undefined);
2317    }
2318
2319    compileFunc(text) {
2320        const { normalize } = this.modulo.registry.utils;
2321        let code = 'var OUT=[];\n'; // Variable used to accumulate code
2322        let mode = 'text'; // Start in text mode
2323        const tokens = this.tokenizeText(text);
2324        for (const token of tokens) {
2325            if (mode) { // If in a "mode" (text or token), then call mode func
2326                const result = this.modes[mode](token, this, this.stack);
2327                if (result) { // Mode generated text output, add to code
2328                    const comment = !this.disableComments ? '' :
2329                        ' // ' + JSON.stringify(normalize(token).trim());
2330                    code += `  ${ result }${ comment }\n`;
2331                }
2332            }
2333            // FSM for mode: ('text' -> null) (null -> token) (* -> 'text')
2334            mode = (mode === 'text') ? null : (mode ? 'text' : token);
2335        }
2336        code += '\nreturn OUT.join("");'
2337        const unclosed = this.stack.map(({ close }) => close).join(', ');
2338        this.modulo.assert(!unclosed, `Unclosed tags: ${ unclosed }`);
2339        return code;
2340    }
2341
2342    render(renderObj) {
2343        if (!this.renderFunc) { // Run module and get function
2344            this.renderFunc = this.modulo.assets.require(this.conf.DefinitionName);
2345        }
2346        return this.renderFunc(Object.assign({ renderObj }, renderObj), this);
2347    }
2348}, {
2349    TemplatePrebuild: "y", // TODO: Refactor
2350    DefFinalizers: [ 'FilterContent', 'TemplatePrebuild' ],
2351    FilterContent: 'trim|tagswap:config.component.tagAliases',
2352    // textFilter: 'escape|etc', // TODO
2353    // TODO: Consider reserving "x" and "y" as temp vars, e.g.  // (x = X, y = Y).includes ? y.includes(x) : (x in y)
2354    opTokens: '==,>,<,>=,<=,!=,not in,is not,is,in,not,gt,lt',
2355    opAliases: {
2356        '==': 'X === Y',
2357        'is': 'X === Y',
2358        'gt': 'X > Y',
2359        'lt': 'X < Y',
2360        'is not': 'X !== Y',
2361        'not': '!(Y)',
2362        'in': '(Y).includes ? (Y).includes(X) : (X in Y)',
2363        'not in': '!((Y).includes ? (Y).includes(X) : (X in Y))',
2364    },
2365});
2366
2367modulo.config.template.modeTokens = [ '{% %}', '{{ }}', '{# #}' ];
2368modulo.config.template.modes = {
2369    '{%': (text, tmplt, stack) => {
2370        const tTag = text.trim().split(' ')[0];
2371        const tagFunc = tmplt.tags[tTag];
2372        if (stack.length && tTag === stack[stack.length - 1].close) {
2373            return stack.pop().end; // Closing tag, return it's end code
2374        } else if (!tagFunc) { // Undefined template tag
2375            throw new Error(`Unknown template tag "${tTag}": ${text}`);
2376        } // Normal opening tag
2377        const result = tagFunc(text.slice(tTag.length + 1), tmplt);
2378        if (result.end) { // Not self-closing, push to stack
2379            stack.push({ close: `end${ tTag }`, ...result });
2380        }
2381        return result.start || result;
2382    },
2383    '{#': (text, tmplt) => false, // falsy values are ignored
2384    '{{': (text, tmplt) => `OUT.push(G.escapeText(${tmplt.parseExpr(text)}));`,
2385    text: (text, tmplt) => text && `OUT.push(${JSON.stringify(text)});`,
2386};
2387
2388// TODO: Tidy up: Move most or all definitions loose like modes
2389modulo.config.template.defaultFilters = (function () {
2390    const { get } = modulo.registry.utils;
2391    const safe = s => Object.assign(new String(s), { safe: true });
2392    const escapeForRE = s=> s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
2393    const trim = (s, arg) => { // General purpose string trimming function
2394        arg = arg ? escapeForRE(arg).replace(',', '|') : '|';
2395        return s.replace(new RegExp(`^\\s*${ arg }\\s*$`, 'g'), '');
2396    };
2397    const tagswap = (s, arg) => {
2398        arg = typeof arg === 'string' ? arg.split(/\s+/) : Object.entries(arg);
2399        for (const row of arg) { // Loop through each replacement pair
2400            const [ tag, val ] = typeof row === 'string' ? row.split('=') : row;
2401            const swap = (a, prefix, suffix) => prefix + val + suffix;
2402            //s = s.replace(RegExp('(</?)' + tag + '(\s|>
2402)', 'gi'),  swap);
2403            s = s.replace(RegExp('(</?)' + tag + '(\\s|>)', 'gi'),  swap);
2404        }
2405        return safe(s); // Always mark as safe, since for HTML tags
2406    };
2407    const filters = {
2408        add: (s, arg) => s + arg,
2409        allow: (s, arg) => arg.split(',').includes(s) ? s : '',
2410        camelcase: s => s.replace(/-([a-z])/g, g => g[1].toUpperCase()),
2411        capfirst: s => s.charAt(0).toUpperCase() + s.slice(1),
2412        combine: (s, arg) => s.concat ? s.concat(arg) : Object.assign({}, s, arg),
2413        default: (s, arg) => s || arg,
2414        divisibleby: (s, arg) => ((s * 1) % (arg * 1)) === 0,
2415        dividedinto: (s, arg) => Math.ceil((s * 1) / (arg * 1)),
2416        escapejs: s => JSON.stringify(String(s)).replace(/(^"|"$)/g, ''),
2417        first: s => Array.from(s)[0],
2418        join: (s, arg) => (s || []).join(arg === undefined ? ", " : arg),
2419        json: (s, arg) => JSON.stringify(s, null, arg || undefined),
2420        last: s => s[s.length - 1],
2421        length: s => s.length !== undefined ? s.length : Object.keys(s).length,
2422        lower: s => s.toLowerCase(),
2423        multiply: (s, arg) => (s * 1) * (arg * 1),
2424        number: (s) => Number(s),
2425        pluralize: (s, arg) => (arg.split(',')[(s === 1) * 1]) || '',
2426        skipfirst: (s, arg) => Array.from(s).slice(arg || 1),
2427        subtract: (s, arg) => s - arg,
2428        truncate: (s, arg) => ((s && s.length > arg*1) ? (s.substr(0, arg-1) + '…') : s),
2429        type: s => s === null ? 'null' : (Array.isArray(s) ? 'array' : typeof s),
2430        renderas: (rCtx, template) => safe(template.render(rCtx)),
2431        reversed: s => Array.from(s).reverse(),
2432        upper: s => s.toUpperCase(),
2433        yesno: (s, arg) => `${ arg || 'yes,no' },,`.split(',')[s ? 0 : s === null ? 2 : 1],
2434    };
2435    const { values, keys, entries } = Object;
2436    const extra = { trim, tagswap, get, safe, values, keys, entries };
2437    return Object.assign(filters, extra);
2438})();
2439
2440modulo.config.template.defaultTags = {
2441    'debugger': () => 'debugger;',
2442    'if': (text, tmplt) => {
2443        // Limit to 3 (L/O/R)
2444        const [ lHand, op, rHand ] = tmplt.parseCondExpr(text);
2445        const condStructure = !op ? 'X' : tmplt.opAliases[op] || `X ${op} Y`;
2446        const condition = condStructure.replace(/([XY])/g,
2447            (k, m) => tmplt.parseExpr(m === 'X' ? lHand : rHand));
2448        const start = `if (${condition}) {`;
2449        return { start, end: '}' };
2450    },
2451    'else': () => '} else {',
2452    'elif': (s, tmplt) => '} else ' + tmplt.tags['if'](s, tmplt).start,
2453    'comment': () => ({ start: "/*", end: "*/"}),
2454    'include': (text) => `OUT.push(CTX.${ text.trim() }.render(CTX));`,
2455    'for': (text, tmplt) => {
2456        // Make variable name be based on nested-ness of tag stack
2457        const { cleanWord } = modulo.registry.utils;
2458        const arrName = 'ARR' + tmplt.stack.length;
2459        const [ varExp, arrExp ] = text.split(' in ');
2460        let start = `var ${arrName}=${tmplt.parseExpr(arrExp)};`;
2461        // TODO: Upgrade to for...of loop (after good testing)
2462        start += `for (var KEY in ${arrName}) {`;
2463        const [keyVar, valVar] = varExp.split(',').map(cleanWord);
2464        if (valVar) {
2465            start += `CTX.${keyVar}=KEY;`;
2466        }
2467        start += `CTX.${valVar ? valVar : varExp}=${arrName}[KEY];`;
2468        return { start, end: '}'};
2469    },
2470    'empty': (text, {stack}) => {
2471        // Make variable name be based on nested-ness of tag stack
2472        const varName = 'G.FORLOOP_NOT_EMPTY' + stack.length;
2473        const oldEndCode = stack.pop().end; // get rid of dangling for
2474        const start = `${varName}=true; ${oldEndCode} if (!${varName}) {`;
2475        const end = `}${varName} = false;`;
2476        return { start, end, close: 'endfor' };
2477    },
2478};
2479
2480modulo.register('processor', function contentCSV (modulo, def, value) {
2481    const parse = s => s.trim().split('\n').map(line => line.trim().split(','));
2482    def.Code = 'return ' + JSON.stringify(parse(def.Content || ''));
2483});
2484
2485modulo.register('processor', function contentJS (modulo, def, value) {
2486    const tmpFunc = new Function('return (' + (def.Content || 'null') + ');');
2487    def.Code = 'return ' + JSON.stringify(tmpFunc()) + ';'; // Evaluate
2488});
2489
2490modulo.register('processor', function contentJSON (modulo, def, value) {
2491    def.Code = 'return ' + JSON.stringify(JSON.parse(def.Content || '{}')) + ';';
2492});
2493
2494modulo.register('processor', function contentTXT (modulo, def, value) {
2495    def.Code = 'return ' + JSON.stringify(def.Content);
2496});
2497
2498modulo.register('processor', function dataType (modulo, def, value) {
2499    if (value === '?') { // '?' means determine based on extension
2500        const ext = def.Src && def.Src.match(/(?<=\.)[a-z]+$/i);
2501        value = ext ? ext[0] : 'json';
2502    }
2503    def['Content' + value.toUpperCase()] = value;
2504});
2505
2506modulo.register('processor', function filterContent (modulo, def, value) {
2507    if (def.Content && value) { // Check if active (needs truthy value)
2508        // value = value.trim().replace(/\s*\n\s*\|/gi, '|'); // Would allow multi-line
2509        const miniTemplate = `{{ def.Content|${ value }|safe }}`;
2510        const tmplt = new modulo.registry.cparts.Template(miniTemplate);
2511        def.Content = tmplt.render({ def, config: modulo.config });
2512    }
2513});
2514
2515modulo.register('processor', function code (modulo, def, value) {
2516    if (def.DefinitionName in modulo.assets.nameToHash) {
2517        console.error("ERROR: Duped def:", def.DefinitionName);
2518        return;
2519    }
2520    // TODO: Refactor this inline, change modulo.assets into plain object
2521    modulo.assets.define(def.DefinitionName, value);
2522});
2523
2524modulo.register('processor', function codeTemplate (modulo, def, value) {
2525    if (def.DefinitionName in modulo.assets.nameToHash) {
2526        console.error("ERROR: Duped def:", def.DefinitionName);
2527        return;
2528    }
2529    const tmplt = new modulo.registry.cparts.Template(value); // Compile Template
2530    modulo.assets.define(def.DefinitionName, tmplt.render({ modulo, def }));
2531});
2532
2533modulo.register('cpart', class StaticData {
2534    static RequireData (modulo, def, value) {
2535        def.data = modulo.assets.require(def[value]); // TODO: Remove asset
2536    }
2537    prepareCallback() {
2538        return this.conf.data;
2539    }
2540}, {
2541    DataType: '?', // Default behavior is to guess based on Src ext
2542    RequireData: 'DefinitionName',
2543    DefLoaders: [ 'DefTarget', 'DefinedAs', 'DataType', 'Src', 'FilterContent' ],
2544    DefBuilders: [ 'ContentCSV', 'ContentTXT', 'ContentJSON', 'ContentJS', 'Code', 'RequireData' ],
2545});
2546
2547modulo.register('coreDef', class Configuration { }, {
2548    DefTarget: 'config',
2549    DefLoaders: [ 'DefTarget', 'DefinedAs', 'Src|SrcSync', 'Content|Code', 'DefinitionName|MainRequire' ],
2550});
2551
2552modulo.config.script = {
2553    lifecycle: null,
2554    DefBuilders: [ 'Content|AutoExport', 'CodeTemplate' ],
2555    CodeTemplate: `var script = { exports: { } };{% if def.locals.length %}
2556    var {{ def.locals|join:',' }};{% endif %}
2557    {{ def.tempContent|safe }}
2558    ;return { exports: script.exports,
2559        {% for n in def.exportNames %}
2560            "{{ n }}": typeof {{ n }} !== "undefined" ? {{ n }} : undefined,
2561        {% endfor %}
2562        setLocalVariables: function (o) {
2563            {% for n in def.locals %}{{ n }} = o.{{ n }};{% endfor %}
2564        }
2565    }`.replace(/\s\s+/g, ' '),
2566};
2567
2568modulo.register('cpart', class Script {
2569    static AutoExport (modulo, def, value) {
2570        const { getAutoExportNames } = modulo.registry.utils;
2571        if (def.lifecycle && def.lifecycle !== 'initialized') {
2572            value = `function ${ def.lifecycle }Callback (renderObj) {${ value }}`;
2573        }
2574        const { ChildrenNames } = modulo.definitions[def.Parent] || { };
2575        const sibs = (ChildrenNames || []).map(n => modulo.definitions[n].Name);
2576        sibs.push('component', 'element', 'cparts'); // Add in extras
2577        def.exportNames = def.exportNames || getAutoExportNames(value); // Scan names
2578        def.locals = def.locals || sibs.filter(name => value.includes(name));
2579        def.Directives = def.exportNames.filter(s => s.match(/(Unmount|Mount)$/));
2580        def.tempContent = value; // TODO: Refactor AutoExport + CodeTemplate
2581    }
2582
2583    static factoryCallback(renderObj, def, modulo) {
2584        //modulo.assert(results || !def.Parent, 'Invalid script return');
2585        const hash = modulo.assets.nameToHash[def.DefinitionName];
2586        const func = () => modulo.assets.modules[hash].call(window, modulo);
2587        if (def.lifecycle === 'initialized') {
2588            return { initializedCallback: func }; // Attach as callback
2589        }
2590        return func(); // Otherwise, should run in static context (e.g. now)
2591    }
2592
2593    initializedCallback(renderObj) {
2594        // Create all lifecycle callbacks, wrapping around the inner script
2595        const script = renderObj[this.conf.Name];
2596        this.eventCallback = (rObj) => { // Create eventCallback to set inner
2597            const vars = { element: this.element, cparts: this.element.cparts };
2598            const setLocal = script.setLocalVariables || (() => {});
2599            setLocal(Object.assign(vars, rObj)); // Set inner vars (or no-op)
2600        };
2601
2602        if (script.initializedCallback) { // If defined, trigger inner init
2603            this.eventCallback(renderObj); // Prep before (used by lc=false)
2604            Object.assign(script, script.initializedCallback(renderObj));
2605            this.eventCallback(renderObj); // Prep again (used by lc=initialize)
2606        }
2607
2608        const isCallback = /(Mount|Unmount|Callback)$/;
2609        for (const cbName of Object.keys(script)) {
2610            if (cbName === 'initializedCallback' || !cbName.match(isCallback)) {
2611                continue; // Skip over initialized (already handled) and non-CBs
2612            }
2613            this[cbName] = arg => { // Arg: Either renderObj or directive obj
2614                const renderObj = this.element.getCurrentRenderObj();
2615                const script = renderObj[this.conf.Name]; // Get new render obj
2616                this.eventCallback(renderObj); // Prep before lifecycle method
2617                Object.assign(script, script[cbName](arg) || {});
2618            };
2619        }
2620    }
2621});
2622
2623
2624modulo.register('cpart', class State {
2625    static factoryCallback(renderObj, def, modulo) {
2626        if (def.Store) {
2627            const store = modulo.registry.utils.makeStore(modulo, def);
2628            if (!(def.Store in modulo.stores)) {
2629                modulo.stores[def.Store] = store;
2630            } else {
2631                Object.assign(modulo.stores[def.Store].data, store.data);
2632            }
2633        }
2634    }
2635
2636    initializedCallback() {
2637        const store = this.conf.Store ? this.modulo.stores[this.conf.Store]
2638                : this.modulo.registry.utils.makeStore(this.modulo, this.conf);
2639        store.subscribers.push(Object.assign(this, store));
2640        this.types = { range: Number, number: Number, checkbox: (val, el) => el.checked };
2641        return store.data; // TODO: Possibly, push ALL sibling CParts with stateChangedCallback
2642    }
2643
2644    bindMount({ el, attrName, value }) {
2645        const name = attrName || el.getAttribute('name');
2646        const val = this.modulo.registry.utils.get(this.data, name);
2647        this.modulo.assert(val !== undefined, `state.bind "${name}" undefined`);
2648        const isText = el.tagName === 'TEXTAREA' || el.type === 'text';
2649        const evName = value ? value : (isText ? 'keyup' : 'change');
2650        if (!(name in this.boundElements)) {
2651            this.boundElements[name] = [];
2652        }
2653        // Bind the "listen" event to propagate to all, and trigger initial vals
2654        const listen = () =>
2654 this.propagate(name, el.value, el);
2655        this.boundElements[name].push([ el, evName, listen ]);
2656        el.addEventListener(evName, listen); // todo: make optional, e.g. to support cparts?
2657        this.propagate(name, val, this); // trigger initial assignment(s)
2658    }
2659
2660    bindUnmount({ el, attrName }) {
2661        const name = attrName || el.getAttribute('name');
2662        if (!(name in this.boundElements)) { // TODO HACK
2663            return console.log('Modulo ERROR: Could not unbind', name);
2664        }
2665        const remainingBound = [];
2666        for (const row of this.boundElements[name]) {
2667            if (row[0] === el) {
2668                row[0].removeEventListener(row[1], row[2]);
2669            } else {
2670                remainingBound.push(row);
2671            }
2672        }
2673        this.boundElements[name] = remainingBound;
2674    }
2675
2676    stateChangedCallback(name, value, el) {
2677        this.modulo.registry.utils.set(this.data, name, value);
2678        if (!this.conf.Only || this.conf.Only.includes(name)) { // TODO: Test
2679            this.element.rerender();
2680        }
2681    }
2682
2683    eventCallback() {
2684        this._oldData = Object.assign({}, this.data);
2685    }
2686
2687    propagate(name, val, originalEl = null) {
2688        const elems = (this.boundElements[name] || []).map(row => row[0]);
2689        const typeConv = this.types[ originalEl ? originalEl.type : null ];
2690        val = typeConv ? typeConv(val, originalEl) : val; // Apply conversion
2691        for (const el of this.subscribers.concat(elems)) {
2692            if (originalEl && el === originalEl) {
2693                continue; // don't propagate to originalEl (avoid infinite loop)
2694            }
2695            if (el.stateChangedCallback) { // A callback was found, use instead
2696                el.stateChangedCallback(name, val, originalEl);
2697            } else if (el.type === 'checkbox') { // Check input use ".checkbox"
2698                el.checked = !!val;
2699            } else { // Normal inputs use ".value"
2700                el.value = val;
2701            }
2702        }
2703    }
2704
2705    eventCleanupCallback() {
2706        for (const name of Object.keys(this.data)) {
2707            this.modulo.assert(!this.conf.AllowNew && name in this._oldData,
2708                `State variable "${ name }" is undeclared (no "-allow-new")`);
2709            if (this.data[name] !== this._oldData[name]) {
2710                this.propagate(name, this.data[name], this);
2711            }
2712        }
2713        this._oldData = null;
2714    }
2715}, {
2716    Directives: [ 'bindMount', 'bindUnmount' ],
2717    Store: null,
2718});
2719
2720modulo.register('utils', class DOMCursor {
2721    constructor(parentNode, parentRival, slots) {
2722        this.slots = slots || {}; // Slottables keyed by name (default is '')
2723        this.instanceStack = []; // Used for implementing DFS non-recursively
2724        this._rivalQuerySelector = parentRival.querySelector.bind(parentRival);
2725        this._querySelector = parentNode.querySelector.bind(parentNode);
2726        this.initialize(parentNode, parentRival);
2727    }
2728
2729    initialize(parentNode, parentRival) {
2730        this.parentNode = parentNode;
2731        this.nextChild = parentNode.firstChild;
2732        this.nextRival = parentRival.firstChild;
2733        this.keyedChildren = {};
2734        this.keyedRivals = {};
2735        this.activeExcess = null;
2736        this.activeSlot = null;
2737        if (parentRival.tagName === 'SLOT') { // Parent will "consume" a slot
2738            const slotName = parentRival.getAttribute('name') || '';
2739            this.activeSlot = this.slots[slotName] || null; // Mark active
2740            if (this.activeSlot) { // Children were specified for this slot!
2741                delete this.slots[slotName]; // (prevent "dupe slot" bug)
2742                this._setNextRival(null); // Move the cursor to the first elem
2743            }
2744        }
2745    }
2746
2747    saveToStack() { // Creates an object copied with all cursor state
2748        this.instanceStack.push(Object.assign({ }, this)); // Copy to empty obj
2749    }
2750
2751    loadFromStack() { // Remaining stack to "walk back" (non-recursive DFS)
2752        const stack = this.instanceStack;
2753        return stack.length > 0 && Object.assign(this, stack.pop());
2754    }
2755
2756    loadFromExcessKeys() { // There were "excess", unmatched keyed elements
2757        if (!this.activeExcess) { // Convert needed appends and removes to array
2758            const child = Object.values(this.keyedChildren).map(v => [v, null]);
2759            const rival = Object.values(this.keyedRivals).map(v => [null, v]);
2760            this.activeExcess = rival.concat(child); // Do appends before remove
2761        }
2762        return this.activeExcess.length > 0; // Return true if there is any left
2763    }
2764
2765    loadFromSlots() { // There are "excess" slots (copied, but deeply nested)
2766        const name = Object.keys(this.slots).pop(); // Get next ("pop" from obj)
2767        if (name === '' || name) { // Is name valid? (String of 0 or more)
2768            const sel = name ? `slot[name="${ name }"]` : 'slot:not([name])';
2769            const rivalSlot = this._rivalQuerySelector(sel);
2770            if (!rivalSlot) { // No slot (e.g., conditionally rendered, or typo)
2771                delete this.slots[name]; // (Ensure "consumed", if not init'ed)
2772                return this.loadFromSlots(); // If no elem, try popping again
2773            }
2774            this.initialize(this._querySelector(sel) || rivalSlot, rivalSlot);
2775            return true; // Indicate success: Child and rival slots are ready
2776        }
2777    }
2778
2779    hasNext() {
2780        if (this.nextChild || this.nextRival || this.loadFromExcessKeys()) {
2781            return true; // Is pointing at another node, or has unmatched keys
2782        } else if (this.loadFromStack() || this.loadFromSlots()) { // Walk back
2783            return this.hasNext(); // Possibly loaded nodes nextChild, nextRival
2784        }
2785        return false; // Every load attempt is "false" (empty), end iteration
2786    }
2787
2788    _setNextRival(rival) { // Traverse this.nextRival based on DOM or SLOT
2789        if (this.activeSlot !== null) { // Use activeSlot array for next instead
2790            if (this.activeSlot.length > 0) {
2791                this.nextRival = this.activeSlot.shift(); // Pop off next one
2792                this.nextRival._moduloIgnoreOnce = true; // Ensure no descend
2793            } else {
2794                this.nextRival = null;
2795            }
2796        } else {
2797            this.nextRival = rival ? rival.nextSibling : null; // Normal DOM traversal
2798        }
2799    }
2800
2801    next() {
2802        let child = this.nextChild;
2803        let rival = this.nextRival;
2804        //if (!rival && !child && this.hasNext()) { return this.next(); } // TODO ALlow for calling w/o hasNext
2805        if (!rival && this.activeExcess && this.activeExcess.length > 0) {
2806            return this.activeExcess.shift(); // Return the first pair
2807        }
2808        this.nextChild = child ? child.nextSibling : null;
2809        this._setNextRival(rival); // Traverse initially
2810        if (!this._disableKeys) { // Disable all "keyed element" logic
2811            let matchedRival = this.getMatchedNode(child, this.keyedChildren, this.keyedRivals);
2812            let matchedChild = this.getMatchedNode(rival, this.keyedRivals, this.keyedChildren);
2813            if (matchedRival === false) { // Child has a key, but does not match
2814                child = this.nextChild; // Simply ignore Child, and on to next
2815                this.nextChild = child ? child.nextSibling : null; // (1687)
2816            } else if (matchedChild === false) { // Rival has key, but no match
2817                rival = this.nextRival; // IGNORE rival - move on to
2818                this._setNextRival(rival); // (and setup next-next rival)
2819            }
2820            const keyWasFound = matchedRival !== null || matchedChild !== null;
2821            const matchFound = matchedChild !== child && keyWasFound;
2822            if (matchFound && matchedChild) { // Rival matches, but not child
2823                this.nextChild = child; // "Undo" this last "nextChild" (return)
2824                child = matchedChild; // Then substitute the matched instead
2825            }
2826            if (matchFound && matchedRival) {
2827                // Child matches, but not with rival. Swap in the correct one.
2828                this.modulo.assert(matchedRival !== rival, 'Dupe!');
2829                this.nextRival = rival; // "Undo" this last "nextRival"
2830                rival = matchedRival; // Then substitute the matched rival
2831            }
2832        }
2833        return [ child, rival ];
2834    }
2835
2836    getMatchedNode(elem, keyedElems, keyedOthers) {
2837        const key = elem && elem.nodeType === 1 && elem.getAttribute('key');
2838        if (!key) {
2839            return null;
2840        } else if (key in keyedOthers) {
2841            const matched = keyedOthers[key];
2842            delete keyedOthers[key];
2843            return matched;
2844        } else if (key in keyedElems) {
2845            console.warn('MODULO: Duplicate key:', key);
2846        }
2847        keyedElems[key] = elem;
2848        return false;
2849    }
2850});
2851
2852modulo.config.reconciler = {
2853    directiveShortcuts: [ [ /^@/, 'component.event' ],
2854                          [ /:$/, 'component.dataProp' ] ],
2855};
2856modulo.register('core', class Reconciler {
2857    constructor(modulo, def = {}) {
2858        this.modulo = modulo;
2859        this.directives = def.directives || {};
2860        this.directiveShortcuts = def.directiveShortcuts || [];
2861        if (this.directiveShortcuts.length === 0) { // TODO Remove this when tested
2862            this.directiveShortcuts = this.modulo.config.reconciler.directiveShortcuts;
2863        }
2864        this.patch = this.pushPatch;
2865        this.patches = [];
2866    }
2867
2868    parseDirectives(rawName, directiveShortcuts) { //, foundDirectives) {
2869        if (/^[a-z0-9-]+$/i.test(rawName)) {
2870            return null; // if alphanumeric-only, stop right away
2871        }
2872        let name = rawName;
2873        for (const [ regexp, directive ] of directiveShortcuts) {
2874            if (rawName.match(regexp)) { // "Expand" shortcut into full version
2875                name = `[${directive}]` + name.replace(regexp, '');
2876            }
2877        }
2878        if (!name.startsWith('[')) {
2879            return null; // There are no directives, regular attribute, skip
2880        }
2881        // There are directives... time to resolve them
2882        const { cleanWord, stripWord } = this.modulo.registry.utils;
2883        const arr = [];
2884        const attrName = stripWord((name.match(/\][^\]]+$/) || [ '' ])[0]);
2885        for (const directiveName of name.split(']').map(cleanWord)) {
2886            // Skip the bare name itself, and filter for valid directives
2887            if (directiveName !== attrName) {// && directiveName in directives) {
2888                arr.push({ attrName, rawName, directiveName, name })
2889            }
2890        }
2891        return arr;
2892    }
2893
2894    applyPatches(patches) {
2895        for (const patch of patches) { // Simply loop through given iterable
2896            this.applyPatch(patch[0], patch[1], patch[2], patch[3]);
2897        }
2898    }
2899
2900    reconcileChildren(childParent, rivalParent, slots) {
2901        // Nonstandard nomenclature: "rival" is node we wish "child" to match
2902        const cursor = new this.modulo.registry.utils.DOMCursor(childParent, rivalParent, slots);
2903        while (cursor.hasNext()) {
2904            const [ child, rival ] = cursor.next();
2905            const needReplace = child && rival && (
2906                child.nodeType !== rival.nodeType ||
2907                child.nodeName !== rival.nodeName
2908            ); // Does this node to be swapped out? Swap if exist but mismatched
2909
2910            if ((child && !rival) || needReplace) { // we have more rival, delete child
2911                this.patchAndDescendants(child, 'Unmount');
2912                this.patch(cursor.parentNode, 'removeChild', child);
2913            }
2914
2915            if (needReplace) { // do swap with insertBefore
2916                this.patch(cursor.parentNode, 'insertBefore', rival, child.nextSibling);
2917                this.patchAndDescendants(rival, 'Mount');
2918            }
2919
2920            if (!child && rival) { // we have less than rival, take rival
2921                // TODO: Possibly add directive resolution context to rival / child.originalChildren?
2922                this.patch(cursor.parentNode, 'appendChild', rival);
2923                this.patchAndDescendants(rival, 'Mount');
2924            }
2925
2926            if (child && rival && !needReplace) {
2927                // Both exist and are of same type, let's reconcile nodes
2928                if (child.nodeType !== 1) { // text or comment node
2929                    if (child.nodeValue !== rival.nodeValue) { // update
2930                        this.patch(child, 'node-value', rival.nodeValue);
2931                    }
2932                } else if (!child.isEqualNode(rival)) { // sync if not equal
2933                    this.reconcileAttributes(child, rival);
2934                    if (rival.hasAttribute('modulo-ignore')) { // Don't descend
2935                        // console.log('Skipping ignored node');
2936                    } else if (child.isModulo) { // is a Modulo component
2937                        this.patch(child, 'rerender', rival);
2938                    } else { //} else if (!this.shouldNotDescend) {
2939                        cursor.saveToStack();
2940                        cursor.initialize(child, rival);
2941                    }
2942                }
2943            }
2944        }
2945    }
2946
2947    pushPatch(node, method, arg, arg2 = null) {
2948        this.patches.push([ node, method, arg, arg2 ]);
2949    }
2950
2951    applyPatch(node, method, arg, arg2) { // take that, rule of 3!
2952        if (method === 'node-value') {
2953            node.nodeValue = arg;
2954        } else if (method === 'insertBefore') {
2955            node.insertBefore(arg, arg2); // Needs 2 arguments
2956        } else if (method.startsWith('directive-')) {
2957            method = method.substr(10);//'directive-'.length); // TODO: RM prefix (or generalizze)
2958            node[method].call(node, arg); // invoke directive method
2959        } else {
2960            node[method].call(node, arg); // invoke method
2961        }
2962    }
2963
2964    patchDirectives(el, rawName, suffix, copyFromEl = null) {
2965        const foundDirectives = this.parseDirectives(rawName, this.directiveShortcuts);
2966        if (!foundDirectives || foundDirectives.length === 0) {
2967            return; // Fast route: "undefined" means no directives
2968        }
2969        const value = (copyFromEl || el).getAttribute(rawName); // Get value
2970        for (const directive of foundDirectives) {
2971            const dName = directive.directiveName; // e.g. "state.bind", "link"
2972            const fullName = dName + suffix; // e.g. "state.bindMount"
2973            const thisObj = this.directives[dName] || this.directives[fullName];
2974            if (thisObj) { // If a directive matches...
2975                const methodName = fullName.split('.')[1] || fullName;
2976                Object.assign(directive, { value, el });
2977                this.patch(thisObj, 'directive-' + methodName, directive);
2978            }
2979        }
2980    }
2981
2982    reconcileAttributes(node, rival) {
2983        const myAttrs = new Set(node ? node.getAttributeNames() : []);
2984        const rivalAttributes = new Set(rival.getAttributeNames());
2985
2986        // Check for new and changed attributes
2987        for (const rawName of rivalAttributes) {
2988            const attr = rival.getAttributeNode(rawName);
2989            if (myAttrs.has(rawName) && node.getAttribute(rawName) === attr.value) {
2990                continue; // Already matches, on to next
2991            }
2992            if (myAttrs.has(rawName)) { // If exists, trigger Unmount first
2993                this.patchDirectives(node, rawName, 'Unmount');
2994            }
2995            // Set attribute node, and then Mount based on rival value
2996            this.patch(node, 'setAttributeNode', attr.cloneNode(true));
2997            this.patchDirectives(node, rawName, 'Mount', rival);
2998        }
2999
3000        // Check for old attributes that were removed (ignoring modulo- prefixed ones)
3001        for (const rawName of myAttrs) {
3002            if (!rivalAttributes.has(rawName) && !rawName.startsWith('modulo-')) {
3003                this.patchDirectives(node, rawName, 'Unmount');
3004                this.patch(node, 'removeAttribute', rawName);
3005            }
3006        }
3007    }
3008
3009    patchAndDescendants(parentNode, actionSuffix) {
3010        if (parentNode.nodeType !== 1) {
3011            return; // Skip anything that isn't a regular HTML element
3012        }
3013        if (parentNode._moduloIgnoreOnce) {
3014            delete parentNode._moduloIgnoreOnce; // Ensure ignore is deleted
3015            return; // Skip ignored elements
3016        }
3017        const searchNodes = Array.from(parentNode.querySelectorAll('*'));
3018        for (const node of [ parentNode ].concat(searchNodes)) {
3019            for (const rawName of node.getAttributeNames()) { // Do patches
3020                this.patchDirectives(node, rawName, actionSuffix);
3021            }
3022        }
3023    }
3024});
3025
3026modulo.register('util', function getAutoExportNames(contents) {
3027    // TODO: Change exports/Directives/etc to def processor to better expose
3028    const regexpG = /(function|class)\s+(\w+)/g;
3029    const regexp2 = /(function|class)\s+(\w+)/; // hack, refactor
3030    return (contents.match(regexpG) || []).map(s => s.match(regexp2)[2])
3031        .filter(s => s && !Modulo.INVALID_WORDS.has(s));
3032});
3033
3034modulo.register('util', function showDevMenu() {
3035    const cmd = new URLSearchParams(window.location.search).get('mod-cmd');
3036    const rerun = `<h1><a href="?mod-cmd=${ cmd }">&#x27F3; ${ cmd }</a></h1>`;
3037    if (cmd) { // Command specified, skip dev menu, run, and replace HTML after
3038        const callback = () => { window.document.body.innerHTML = rerun; };
3039        const start = () => modulo.registry.commands[cmd](modulo, { callback });
3040        return window.setTimeout(start, modulo.config.commandDelay || 1000);
3041    } // Else: Display "COMMANDS:" menu in console
3042    const href = 'window.location.href += ';
3043    const font = 'font-size: 28px; padding: 0 8px 0 8px; border: 2px solid black;';
3044    const commandGetters = Object.keys(modulo.registry.commands).map(cmd =>
3045        ('get ' + cmd + ' () {' + href + '"?mod-cmd=' + cmd + '";}'));
3046    const clsCode = 'class COMMANDS {' + commandGetters.join('\n') + '}';
3047    new Function(`console.log('%c%', '${ font }', new (${ clsCode }))`)();
3048});
3049
3050modulo.register('command', function build (modulo, opts = {}) {
3051    modulo.preprocessAndDefine(opts.callback, 'BuildCommand');
3052});
3053
3054if (typeof window.moduloBuild === 'undefined') { // Not in a build, try loading
3055    if (typeof window.document !== 'undefined') { // Document exists
3056        modulo.loadString(modulo._DEVLIB_SOURCE, '_artifact'); // Load dev lib
3057        modulo.loadFromDOM(window.document.head, null, true); // Blocking load
3058        window.document.addEventListener('DOMContentLoaded', () => {
3059            modulo.loadFromDOM(window.document.body, null, true); // Async load
3060            modulo.preprocessAndDefine(modulo.registry.utils.showDevMenu);
3061        });
3062    } else if (typeof module !== 'undefined') { // Node.js
3063        module.exports = { modulo, window };
3064    }
3065}
3066
3067modulo.assets.modules = window.moduloBuild.modules;
3068modulo.assets.nameToHash = window.moduloBuild.nameToHash;
3069modulo.definitions = window.moduloBuild.definitions;
3070
3071
3072modulo.assets.require("configuration");
3073
3074modulo.assets.require("configuration1");
3075
3076modulo.assets.require("library_configuration");
3077
3078modulo.assets.require("library1_configuration");
3079
3080modulo.assets.require("library_Page");
3081
3082modulo.assets.require("library_FeaturedTestimonials");
3083
3084modulo.assets.require("library_Testimonials");
3085
3086modulo.assets.require("library_FeaturedArticles");
3087
3088modulo.assets.require("library1_StudentIDGenerator");
3089
3090modulo.assets.require("library1_HiddenSuccessCard");
3091
3092modulo.assets.require("library1_PlanMenu");
3093
3094modulo.assets.require("library1_Courses");
3095

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.