 Okay, migrating Flex to FlexJS. Okay. All right. Who am I? I kind of fell into programming by mistake. I had a print shop and had some problems with InDesign, didn't do what I wanted it to do, so I figured out how to make it do what I wanted it to do. So that led me into developing different solutions around InDesign. I started marketing them. I fell into Flex when I needed to extend the design with panels. That was about nine years ago. Adobe has since disabled flash panels, which was what I was doing there. So FlexJS kind of became pretty central about things that I've been doing lately, along with my UI application, which has also had to be migrated from Flex to FlexJS. So I've had lots of reasons to migrate from Flex to FlexJS, and that's basically what I've been doing for the last year. Okay. What kind of name is Harb's? Harb's is my nickname I've held since I was a teenager. My given name is Gabriel Harbiter. If you attend my family gatherings, you try to call Harb's, lots of people turn around. But that's kind of what I go by. How easy is migration? Well, I guess it depends on what you're trying to migrate. If basically the better separation you have between view and model and things like that, the easier it's going to be. The more it's all mixed together, the more the flash APIs are kind of integral to what you're doing, and the harder it's going to be. Okay. So why did I pick FlexJS rather than just using a normal JavaScript framework? The answer is I tried, actually. I spent a year creating a JavaScript quote-unquote version of our web application for modifying and designing documents using Angular. Very basic in terms of functionality, very slow in terms of performance, really big pain to maintain. So we spent a good solid year on it and had very little to show for it. That kind of cemented into my mind that I definitely don't want Angular and a kind of disinterested me in pursuing most of the standard JavaScript frameworks. So FlexJS kind of became the natural next step. Okay, so how do I know migrating to FlexJS is actually possible when it works and something that you want to do? Okay, so far to date I've done basically three major migrations from Flex application to FlexJS. We have a large scale of application which is basically InDesign Online, which is basically a design application for manipulating text and images and objects and producing output of JPEGs, BDS, things like that. We've migrated the vast majority of the application to FlexJS already. We still have some less than five major features that still need to be migrated. We still need to be completed before we're going to call the first version and feature complete, but it's been working very well. We've been doing this together with Yeshai, and I think we both think that it's gone pretty well. I have a number of flash panels in InDesign that need to be migrated. I've done the first one, and I've migrated the TLF framework, which is partially functional right now. It does compile, it does work on a basic level. It needs a text engine. I've created one, I created a half of one which I donated to Flex, another one, three quarters of one, which I'm probably not donating at least for now to Flex. It's sort of working now, not completely. Hopefully by the time we're done, it'll be better shape. How long does it actually take to migrate? The web app, which has been working on it for about a year, is still working on it. I'll go into a little bit more about how the different stages of the migration and how long each part took, and I'll go into that a little bit more later. The InDesign panel took about two months. Most of that was related to problems specific to InDesign. The flash panels allowed synchronous communication between the InDesign scripting model, basically using external object. This very flash allows for very transparent communication with outside scripting. The Chromium embedded framework that InDesign uses does not basically, there's a V8 version of JavaScript that's running in its own world, and there's an event model, basically, which communicates between the two different worlds. So it's all asynchronous. Most of the work that was involved in migrating the InDesign panel was due to this migrating synchronous code to asynchronous code, which was a big mess. The migration of TLF took about a month. Most of that time was due to compiler issues, which are mostly fixed at this point. Okay, what were our challenges? What were the things that we needed to get done before the migration was possible? So first of all, the application does determine this amount of reading and writing XML. So for me, XML and E4X was a very big one. I didn't even bother trying to do the conversion before FluxJS supported XML and E4X. That was what I was working on about a year ago. I was working on the Flux side. Alex was doing most of the compiler work on that. So that's working pretty well at this point. There's a lot of transformations. We need to cross-browser rendering in terms of that. We had to figure out whether we wanted to do Canvas or SVG. We settled on SVG. I hope it was the right decision. We needed TLF, which I've been working on recently. We needed to write a new text engine from scratch. That's been fun. And FluxJS is still not completely mature, just touched on some of the issues that he came across. So we had a lot of work in the actual FluxJS framework, fixing things, adding components, and improving things. So what works and what doesn't? What should you try to migrate as is? What should you try to rebuild? So basically business logic, from what we've seen, basically you could just bring it in 100%, take your logic, copy it over, and 99% of the time it just works. That's been great. Abstract UI logic. So different pieces that are connected to each other. Things that are not specific to the way components are implemented. That stuff generally migrates pretty well also. Some of the interaction, so there are differences in the components and the way the components are composed and the way the components are built, the way the components are connected to each other. So certain things work. There's lots of similarities between it to the frameworks and in places where it's similar, you can copy that logic over. What doesn't work? Don't try to just take your existing UI components and just bring them over, because it's going to create more work than if they're building it from scratch. The differences in the paradigms of classic Flux and FluxJS is just too different. You have to get used to the new concepts with strands and beads and things that did certain things in the old Flux don't necessarily do the same things in the new Flux. So you really want to rethink your UI, how it's structured, which components you need, and just build them out from scratch. If you're migrating, you probably have a lot of cruft in your old code, so it's a good time to clean it out anyway, so not the end of the world. What do you have to actually do it? I tried to break it down into steps. As far as I can tell, there's basically eight steps. Number one, using the IDE. Try to do this without an IDE. You're going to be here. Don't bother. The first thing you want to do is, the most errors you're going to get isn't going to mean your view. So if you can, you want to try to disconnect your view. Concentrate on first making the code compile. Don't worry whether it actually works. Just try to make it compile. Create dummy classes. I'll get more into that later. Then reattach the view. Create dummy classes for the view. Rebuild your components. And then fill in your dummies. ID. So I started with the Flash Builder. It works when it doesn't crash. I've used FDT as well. Most of the conversion, most of what I did with TLF when I was converting TLF was in FDT. I fixed all compiler components there. If you liked IntelliJ, wonderful. I haven't managed to really get it to work for me yet. Probably because I didn't invest enough time in it. Apparently Flash Develop works for Windows. Again, I haven't tried that. Moonshine is something else I haven't tried. And I'm a big VS Code fan. Yeah. So VS Code is what I'm basically pretty much using now. For migration, I haven't tried since the newest features in VS Code, but you're going to have a lot of import cleanups to do. You're going to have a lot of find and replaces and reporting bug errors and whatever. You probably can use VS Code at this point. When I was doing my configuration, VS Code wasn't mature enough at that point. But if the next migration I was doing, I would probably just try to do it in VS Code and see how that goes. Okay. So as far as disconnecting the view, the original application was built using PureView MVC. So that was really actually worked out very well because in bootstrapping the application, so PureMVC have commands that basically adds all the views. So all we needed to do to disconnect the entire view from the application was to just comment out the command which creates the view. And that took away the entire dependency list on anything in our view. So we were able to compile the application with just the business logic, and though the view was just commenting out a few lines of code. So that was really, really, really good. Obviously, if the view is more intricately connected, so then it's going to be a little more difficult. But as much as you can disconnect the view at first, it's going to be easier because you can have to be building that view anyway. Okay. Concentrating on making code compile, okay? You're going to get lots of errors. When we first just ported it over and disconnected the view, we had like 3,000 errors, something like that. But fixing these compilation errors, as overwhelming as I might see, it allows you to focus on, okay, you just go through the errors and take this error away, take that error away, take that error away. You'll find that a lot of the errors are just repeating ones as soon as you create a dummy class, which is basically an empty class which just gives the compiler a reference. So as soon as you add these classes and you add these methods, it makes lots of errors go at the same time. Very often it causes new errors because there's dependencies on each other. But generally the number of errors goes down. And it's also not quite as bad as it might seem at first. It's really mindless work. You know, just find this error, okay, this error, creating the thing. But it's strangely satisfying. It's kind of like angry birds, you know. Just squash those things. Okay, creating dummy classes. So basically it's two-step process. Each time you encounter a class, generally it's going to be either flash or flex for the most part. You take the import statement, create a new class, and search and replace all your imports to import this new one. So some import statements are going to kind of smart the dummy classes. Others, FlexJS has equivalence. So mouse events, events, matrix, point, rectangle, all these things basically. You're going to transfer the flash ones to the FlexJS ones. Things that FlexJS does not have equivalence to, then you're going to have to create new dummy classes for those. So initially the dummy classes should be empty with no methods, no properties. And then basically each time you find a property or a method that you used in that class, just add a stub for that. That'll make the compiler error go away, okay? Why do it like this? There's basically three advantages. It's really, really quick to be able to get the compiler, the code to compile. I was able to get the application from the point of the initial, this is an application with literally hundreds of classes. I was able to get lots of very deep integration into flash drawing commands, TLF, it was nuts. So I was able to get it from the point of the initial migration to be able to compile, in about two weeks. I was surprised by how fast we were able to do it. So let's get it to the point where it compiles so you can start actually making it functional. It gives you a list of what you actually used. So trying to get an audit of what your problems are in your application is a very difficult thing to do. So by creating these dummy classes, first of all you have a list of classes which you used and what kind of functionality you're going to have to fill in. And then it also gives you a list of actual functionality within those classes that you actually used. So you don't necessarily have to reimplement an entire flash class with 75 APIs when you only use two or three of them. Next step is reattaching the view. You're going to create a whole new class of errors. You're probably going to want to create dummy classes for everything that's in an MXML file because you're going to want to rebuild those. Again, that will give you a very clear idea of what your external APIs are on that. Create a dummy class for you and then you're going to rebuild the components. So you're going to want to create new files for that, new XML files, new AS files if you want. So what we did, which worked well, was we kept the old files. We just dumped them all into an external folder just to have them as a reference. We used it as a loose template, but basically looked over trying to figure out exactly what we need from them. And then most of the functions in the script block we were able to just copy over with caveats. And that worked pretty well. Right. So if you keep your old app in your environment where you can actually run it and debug it and see what it was supposed to do and then make sure that the new implementation does the same thing, that's also helpful. So the last step basically is you need to replace the missing classes. Very often you can find FlexJS classes which do the same thing, sometimes in a slightly different way. Sometimes you're going to have to find your own way of doing things and you're going to have to get your hands dirty and actually implement things. What was the hardest part? Number one, FlexJS was missing lots of things. We had a very heavy dependency on SVG. SVG was kind of shaky. Still not what I'd call dirty but it's less shaking than it was. So we had to implement a lot of that. I had to implement a lot of things that should have worked, that didn't work, could have worked. So that was a big, big part of the effort because FlexJS is young. The component set is not quite complete so we had to fill things in as we went along. The other part which was nice about Flash was there's very few browser dependent issues. There are just no end to them. Things that are supposed to work and just don't. One of the things that just boggle my mind is just transformed SVG don't get mouse events on most browsers. Which browsers do you have the most problems? Probably Internet Explorer. I think it should be supporting I-10. I think so for the most part. I'm not sure. We haven't tested I-10 well enough. After we test I-10 we're going to decide exactly which version we should support. That might help determine because we are using MDL. MDL was actually very timely for us when MDL was developed when we needed it. Thanks guys. There's still issues. Just one small one. This is an actual application that we're working on. This is all SVG stuff images. This is text rendered by my new text renderer. These are MDL components over here on the side. This is in Chrome. Works great. The application is centered the way it should be using the component that we were using. In Safari, you just kind of bring it over there. But instead of it being centered, the left side is centered. You have all these kinds of issues. This is something we're going to have to figure out why the layout is doing this. But it's just the way it is. I don't see this as a FlexJS problem. I think this is just a general browser development problem. There's just so many inconsistencies across browsers. If anything, FlexJS makes it easy because a lot of these things are kind of done on the framework level. Okay, gotcha. What are things to look out for when you're doing a migration from Flex to FlexJS? Probably number one is circulars. Google Compiler is very finicky about circular dependencies. In Flash, you were able to do almost anything in terms of circulars. It was really very intelligent about the way it compiled things. You can have class A relying on class B, relying back on class A. It can make spaghetti code without a problem. As long as you didn't have true circular of class A, which inherits class B, which inherits class A, you're fine. Google doesn't allow any classes to rely on any classes and back again. Sometimes you can live with that. Very often you can't. Just off the top of my head, two cases. In FlexJS, we support XML and E4X. In E4X, XML knows about XML lists and XML knows about XML. That's a circular. There's really no way to resolve TLF. All over the place, you have circulars. Paragraph elements know about span elements, which know about paragraph elements, which know about text flows, which know about paragraphs, which they just all know about each other. It's one big happy family. Google Compiler doesn't like that. There's a compiler option called removeCircular. Circular, I think there's a missing asset there. Which basically causes the Flex compiler to remove these circulars. Basically what it does is puts in a certain, only in one place. It tells the Google compiler that the class A is dependent on class B and it lies about the fact that class B is dependent on class A. It doesn't tell the Google compiler about that. As long as the classes are loaded in the correct order, that works fine. I ran into lots of problems around this. I think they're basically all fixed at this point. Back and forth with Alex about a month or two ago when I was working on TLF to get this all straightened out. There are certain limitations with the way it's done, but for the most part it works. Related to that, you can check constants and initialize variables. Don't. Actually you can, but as long as you limit yourself to native JavaScript classes, then it's okay. You can do const, name, string, equals whatever it is. That's fine. If you try to do const, you know, my thingy of type thingy, then your application is going to blow up because in JavaScript, any constants or variables that are initialized when they're declared is run as soon as the JavaScript file is loaded. Because of the way the circulars are resolved in the compiler, you have no guarantee which file is loaded before which one. You can be trying to instantiate a class before that class is actually declared. So very simply don't do it. Instead of using constness, use getters. If you're worried about performance, you can cache the getter the first time you run it. And for static variables, use lazy instantiation. It's basically the first time you need it. You should instantiate it. That's pretty much what you need to know in terms of statics. If you stick to those two rules, you should be all right. Be aware of as. So you could use as, for the most part, the way you did in classic action script, basically, you know, you know, my thing as whatever. And if you don't do anything, it will call language.as, which basically checks whether, which calls language.is, which checks what class is. And if it's not that object, it will return an all. You probably do not want to do this in JavaScript, for the most part, because that adds a lot of overhead at runtime. Every single time you're casting something, you're running this check, which has, I don't know, 20 steps or something in certain cases, to check whether or not it is that object and then returning null based on that. So 99% of the time, you don't want to do that in JavaScript. For most uses of as, is just to make the compiler happy. So the way to get clean JavaScript code is to use the compiler option of js output optimizations equals skip as convergence. If you don't want to use that, you can disable the as calls in specific places by putting a comment at the beginning of your function flex js ignore coercion with the name of the class that you want to ignore the coercion for. Alternatively, you can use the compiler option of skip as coercions and in specific cases, what you might want to use as to return a null, if it's not that object, you can use flex js emit coercion to force the compiler to actually add in the and language that as call. Int isn't. Basically, in Flash, you declare something as an int and you do mathematics, which you do four divided by three, or ten divided by three or something. Ten divided by three will give you three. JavaScript, if you do ten divided by three, that'll give you 3.3. Even declaring an int doesn't solve a problem. This is probably something that should be fixed in the compiler if you have a mathematical equation which could return a floating point number. The compiler should probably add in a floor to convert it to an int, but it currently doesn't. Basically, in cases where this is important, you're going to want to floor your result. A very common example of this is a binary search function where you try, you know, in an index in an array of one and a half is not going to get you a result. Yes, I bumped into this in a few places, actually. I fixed it by flooring it, but it's just something that should be fixed. I haven't put in a genre yet for that. Internal classes generally work. Basically, internal class means that if you declare a class at the bottom of your action-script file, it's available to that file. So if it's a simple class, it shouldn't have any problems. If it's a class that inherits some other class, so the compiler doesn't always realize that there's dependencies over there and you could have things blow up. So be aware of that if necessary. You might want to pull them out in separate files. You should also be aware that it might work and then stop working because the order of the files that are loaded might change. Even though the dependency wasn't declared, it might initially work and not work later. XML, in general, is working pretty well. We're using it pretty extensively. The compiler needs to be aware that your XML is XML or eForce X statements will not work. So always declare your XML types. I tried to make XML evaluate to string intelligently, but sometimes you might end up with situations where you're trying to do a comparative or rather an XML list to equals a string and you're going to answer it in JavaScript, it might be no. So there might be occasions where you need to manually do to string. If something doesn't work, let me know. Copying assets, one of the issues we've had is we have had dependency on external JavaScript libraries, so we needed the JS files. Part of it doesn't copy those. I added SVG. It's probably a better way to have copying of files done in the compiler. I think Chris touched on that earlier. So that's probably something that could be improved. Automatic copying of assets. Alright, minification issues. We had lots of those. I don't know if any of you know it's like an Angular dealing with minification, but they have like a really weird... Any time you declare you have a controller, you have to do this funny bracket notation to make the minifier realize that it shouldn't minify that. FlexJS is really smart about how it does it. It tells the compiler to keep what it's supposed to keep, and you don't have to worry about it. So in general, it's really good. Where you've had issues is integration in external libraries. So if you can go ahead and make type defs for your external libraries, then that works pretty well too. It's a pain. In general, we didn't want to do that. That could probably be improved and make it easier to create those type defs. But... So we've had issues with that. Generally, when you do have issues, you just need to, instead of doing dot, whatever, you can do a bracketed... coded bracket call, whatever you call that thing. And then it doesn't get minified, and that'll work. In object literals, you always want to use quotes. So basically, if you have an object... So you don't want to do it like this. You want to quote the property names like that. Because if you don't, they will get minified, passed around to something else, and what it's being passed into is not going to know what it's minified into. So always quote your object property names. So I had a case where I was using a typed object in an external library, which had various properties. So I got this object from external library, and I needed to use it internally. I wanted it to be typed, but I was getting it from this external library. So basically, after a lot of head scratching, and what I ended up doing was the constructor of my object took the external object, and using bracketed notation, I initialized all my local variables with the results of that, and that ended up working. So again, bracketed notation is the way to go when you don't know what you're dealing with. I don't know how much of this can be fixed on the compiler level. There might be ways to tell the Google compiler to improve this, but these are the general issues that you're going to run into. Basically, you're probably going to compile your application. It's going to work in the debug. You can try in the release, and something weird happens. And it's generally because something was renamed that you weren't expecting it to. So these are the things to look out for. It's been fun. That's all I got.