A Primer on Agile/Scrum

This is a primer on the ideas/ls of Agile. They are not what most people think they are, and it would behoove many people to take time to learn what they really say.

The Agile Manifesto:

Individuals and interactions over processes and tools

Working software over comprehensive documentation

Customer collaboration over contract negotiation

Responding to change over following a plan

Declaration of Interdependence:

We …

  • Increase return on investment by — making continuous flow of value our focus.
  • Deliver reliable results by — engaging customers in frequent interactions and shared ownership.
  • Expect uncertainty and manage for it through — iterations, anticipation and adaptation.
  • Unleash creativity and innovation by — recognizing that individuals are the ultimate source of value, and creating an environment where they can make a difference.
  • Boost performance through — group accountability for results and shared responsibility for team effectiveness.
  • Improve effectiveness and reliability through — situationally specific strategies, processes and practices.

Other types of Agile other than Scrum:

  • XP (Extreme Programming) – Focused on partner-programming and deep code reviews
  • TDD (Test Driven Development) – Focused on, well, TDD

Scrum – A type of Agile

  • Transparency – Visibility to people responsible for outcome
  • Inspection – Regularly assess how doing related to goals 
  • Adaptation – change process to fix problems

Defined By: 

  • Do just enough prep before starting work to be able to the work
  • Short sprints, 2-4 Weeks
  • Customer (The Business) must talk with the developers regularly

Team needs:

  • Product Owner – The business rep, maximizes value from sprints
  • Scrum Master – Process owner, ensures SCRUM is followed
  • Dev Team – Everyone on dev team is equal, there are no bosses, though people can work on specific things.

Ceremonies:

  • Sprint Planning Meeting
  • Grooming (technically not a formal Scrum event)
  • Daily Scrum (Standup)
  • Sprint Review – PO and Scrum team and customer meet to demo
  • Retro – Team assess selves

Documents:

  • Product backlog – all work to be done to deliver full product
  • Sprint backlog – work to be done in single sprint – highest value items first
  • Product Increment – Part of overall product that can be released to customers on its own

Fail Fast => Learn Quickly

It’s ok if people make mistakes if they were trying.  The point is for us to do so early before we build lots on top of the broken.  We need to trust each other.  We don’t blame people for mistakes, we are a team, they are team problems, not individual problems

Visibility/Transparency

Definition of “Done”

Sprint Goals – Should be very visible and reviewed often – make sure you meet them

Burn down – Shows work as it gets completed

Burn up chart – Should how us when the product will finish, based on what is in backlog and velocity

Team makes its own rules, solves its own problems.  

Team makes its own definition of “done” – It’s the TEAMS decision if QA should be removed from the “done” requirement

Inspection/Adaptation

The team should be inspecting what they are doing to see if it is working, and change their processes if they are not.

All Scrum events are opportunities to analyze your process and refine it.  

Value Driven Delivery

This is the POINT of Agile – Focus on Value

Product = Value

Value to Customers

What gives most value to customer?

Vertical slicing

Itemize each valuable deliverable

Defining Value

Requires Defining Value – Acceptance Criteria

Agreeing up front – helps minimize waste

FOCUS ON VALUE DELIVERED!

Short Sprints

Short sprints allow you to fail faster.

2 week sprints you fail much faster than 4

Short sprints also show value to stakeholders more often 

Team Size

Team has 5 – 9 people 

Dev team specifically is 3-9 people

Then you need a PO and Scrum master

Up to 12, but that gets to be a challenge, anything beyond that needs to be broken into multiple teams.

Multiple Teams

Scrum of Scrums

With multiple teams, Scrum masters from each team meet 2-3 times a week to coordinate efforts between teams.

Scrum Events:

Events should be timeboxed based on the length of sprints.  If you have longer sprints your events will need to be longer – except Daily Scrum, which is always 15 minutes.

Sprint Planning:

Every sprint should create a working product increment.

What is needed for Sprint Planning Meeting:

  • The Backlog needs to be ordered
  • Items completed in last sprint
  • Velocity of team
  • Dev Teams capacity (is anybody on vacation?)
  1. What can we do that will result in a product increment?
  2. How will we do it?

PO provides a list of possible items for sprint and Dev Team decides which ones to accept, with the mind toward creating a working product increment.

Sprint Goal is defined:

Sprint goal is: Summary of the work and the value that will be delivered in the product increment.

Daily Scrum (standup):

The daily Scrum (also known as standup) is a daily, timeboxed 15 minute meeting, where everyone on the team goes of:

  • What they did yesterday
  • What they are doing today
  • Any roadblocks

Scrum Guide Says:

  • Scrum Master does NOT attend Daily Scrum, they only ensure it happens.
  • The Product Owner ALSO does not attend the Daily Scrum.

Scrum Alliance Says:

  • Entire Scrum Team Addends
  • Any member can provide updates

Team members can meet after Daily Scrum to talk about things that came up.

Backlog Refinement:

This is an ongoing process where the PO gathers information needed for items in the backlog.

ALSO: Backlog Refinement can be a meeting after the midpoint of sprint.  Inspect backlog items and dev team asks for clarification on things targets for next sprint.  Sounds like Octopus Grooming. Scrum Guide does not have this meeting, but Scrum Alliance does.

How to tell if item is ready for a sprint:

  • Sized small enough to fit in a sprint
  • Dependencies are identified and managed
  • Acceptance Criteria has been defined

Sprint Review:

This is the inspect and adapt event for the Scrum team and all the key stakeholders.  Product increment is the subject of the event.  This is the second to last event in the sprint.  Usually held in the afternoon to give the morning to finalize anything. Questions from stakeholders are encouraged!  Timeboxed meeting.  Probably 2 hours.

Things gone over:

  • PO gives sprint goal and issues worked
  • Demo
  • PO goes over issues in next sprint
  • Open discussion with stakeholders – capture feedback
  • Turns feedback into backlog issues

Demo:

Timeboxed!

The point is to demonstrate value to Stakeholders.

Stakeholders need to be at demo!

It is their opportunity to give feedback

Timing demos is important.

The higher the risk the more often you should demo.

Customer feedback need for quality

You can even demo every week instead of at the end of sprints OR Every other sprint!

They do not have to follow the sprints

More often = more informal / less often= more formal

Might make sense to demo before releases, not with sprints

Sprint Demo

Demos the stories completed in the sprint

PO or Devs can demo, then stakeholders give feedback or missing requirements (those are hopefully stories further down the backlog, but might need new stories.)

Product Demo

Show stakeholders the collective product as it is being built

This is a demo that gives a cohesive view of what the product looks like

This should be done for every release

Sprint Retrospective:

Focused on Scrum team.  This meeting is about how the team can make improvements for themselves and their process.

Things to review:

  • What went well – consider why they went well and how to keep momentum going
  • What didn’t go well – why didn’t they go well and how can we improve that

Then look at both lists and decide what to focus on in next sprint – you should only pick 2-3 ites to work on in the next sprint, or it will become too much.  These need to stay visible to team through sprint.

Release Need to be Valuable:

MVP – Minimum Viable Product

MMF – Minimum Marketable Feature

MVP = MMF

MVP Advantage – 

  • Feedback sooner
  • Early ROI

Backlog Prioritization

Backlog MUST stay prioritized with what the customer values most as the highest priority

It is the PO’s responsibility to organize the backlog – But they get their demands from the stakeholders.

The backlog exists as long as the product exists.

Items in the backlog are Product Backlog Items, or PBIs

Backlog contains:

  • Features
  • Functionality
  • Requirements
  • Defects
  • Enhancements

Prioritization Options:

Kano:

  • Exciters
  • Satisfiers
  • Dissatisfies
  • Indifferent Features

MoSCoW:

  • Must Have
  • Should Have
  • Could Have
  • Would like to Have

Lots of other ways you can do it too.

Sprint Backlog:

Subset of backlog with the items that are to be completed in the sprint.

The Dev Team NOT the PO decides what is to go into the Sprint Backlog.  This is because the goal is to create a working product increment, and the Dev Team is best suited to identify what PBIs can create a working product increment. 

Decision Making

Everyone is involved in decision making, and you are looking for convergence, team and stakeholders.

Agile Charter

Focuses on project goals, not what will be built.

  • Who will be engaged in project?
  • What will the project be about?
  • Where will the work happen?
  • When will the work happen?
  • Why was the project needed?
  • How will you work?

Need Vision

Need Definition of “Done”

Agile Modeling

Ways to model things, and get feedback from stakeholders – Should be used every time you start work on new valuable deliverables

  • Diagram use cases
  • Model data structures
  • Sketch screen designs
  • Wireframe
  • Personas – User use description

Status updates

Status reports suck – don’t use them

There are the charts (burndown, etc.)

Need to have conversation with stakeholders

What to Communicate

  • Charter
  • Vision
  • Sprints
  • Release Plan
  • Burn Charts
  • Project Risks
  • Quality/Defect stats

Forecasting short term results is a lot easier than long term (duh)

Product Demo

Show stakeholders the collective product as it is being built

This is a demo that gives a cohesive view of what the product looks like

This should be done for every release.

Adaptive Planning

Constant planning – planning to plan

You are planning every day.

Everything is planning.

Progressive Elaboration

Incorporating new information into plans to reprioritize backlog.

Every time you release an MVP you need to reevaluate all of your previous plans – did the process change any of your plans?  This should be a big meeting with stakeholders where everyone gets to have input on how priorities have changed.  Then the backlog will need to be reprioritized. 


Possible Additions

Iteration 0 (Sprint 0)

Iteration 0 an option addition that is basically a sprint before the first sprint that allows you do to things such as:

  • Identify resources
  • Setup workspaces
  • Setting up environments
  • Establishing a vision
  • Early planning
  • Hiring
  • Training

Doesn’t have to be length of full sprint

Iteration H (Hardening Sprint)

Hardening, finalizing, testing, documenting –  before product moved to production

Architectural Spike

Short (like 1 week) while team investigates PoC

Risk-Based Spike

Used to test new-untested technology

Relative Sizing

Used because people are better at comparative sizing than actual sizing

Fibonacci – follows normal growth patterns and normally used

Estimation Techniques

Wideband Delphi: Team anonymously submit estimates to avoid bias (not actually used much)

Planning Poker: Most widely used (would have to research)

Team Roles:

Role: Project Sponsor

Usually senior leadership and not directly involved day-to-day

  • Main Project Advocate
  • Final Decision Maker

Role: Product Owner

Soul accountable party for the product the team is building.

Should be a person from The Business.

Need to be able to answer Dev Team questions.

Need to be able to negotiate backlog items.

Must be able to talk with other people in business.

Makes the decisions about product vision and the features that are needed.

Represents stakeholders.  

Dev team only takes orders from PO.

Responsible for maximizing value.

PO -AND ONLY THE PO- manages the backlog.

A proxy can be used if the PO can’t dedicate the full time – but the PO is ultimately responsible. 

A Business Analyst can work with the PO to help them prioritize for the business.

Though the PO is responsible for the backlog they can delegate responsibility to parts of the Dev Team, especially in the case of technical issues they don’t understand.  

PO Responsibilities

  • Clearly define Backlog issues
  • Order/Manage Backlog
  • Ensure Backlog is available to the team, and across the Business.
  • Clarifying requirements
  • Providing status and forecast

PO Can abnormally terminate a sprint early – usually done when current work is found to have no value, and should be stopped.

PO does NOT manage the team – only the backlog

Role: Scrum Master

NOT the manager of the team

They manage the Scrum process, NOT the team

They instruct everyone – Dev team, Product Owner, Business – on Scrum process

They are the SERVANT of everyone

  • Protects team from outside distraction
  • Protects team from overcommitting
  • Protects team from complacency – makes sure team keeps refining and improving their process
  • Owns the Scrum framework – They are the ones who actually decide how/if process can be changed

Scrum Master’s Jobs

  • Clarify Goals and Objectives
  • Coach Best Practices
  • Guide Process and Planning
  • Facilitate Scrum Events
  • Share Knowledge

SM has their own Scrum event – Product Backlog Refinement

Role: Development Team

  • Builds product

Role: Stakeholder

Who are the stakeholders?

PO, Scrum master, team, and sponsor – brainstorm on this is.

Who are direct users of the product?

Executives, manager, who in the company?

The stakeholders need to be told about Agile.

They need to be present in demos.

Definition of Done

The Definition of Done is something that should constantly be reviewed.  This is different from the Acceptance Criteria of a PBI, ACs are different for different PBIs, DoD is across the board for all PBIs.  Teams should have a defined definition of done for all PBIs, and constantly be reviewing it. Possible DoDs include:

  • Unit tests written
  • Reviewed by another developer
  • Passes linting

Team Norms

Ground rules created by team to guide the project and how team members interact.

Norms should be reviewed and updated every couple months – maybe after every MVP goes live?  Should spend an hour in a norming session. 

Possible rules:

  • Silence means consent – if you disagree, speak up
  • No meetings before noon on mondays, or after noon on Fridays
  • All opinions and ideas will be considered equally and thoughtfully
  • Team members will ask for help as soon as they know they need it
  • Team members will keep commitments to each other
  • Team members will hold each other accountable for maintaining norms
  • People will talk respectfully and thank each other for contributions
  • No devices in meetings without team consent
  • All decision made by consensus
  • Agree to try to resolve conflicts inside of team before going outside of team

Skills

Though everyone should have specific skills, team member should have general skills too, to be able to help with other tasks

Swarming

When team members use secondary skills to help reach the sprint goal.

Collaboration and Commitment

The space you are in can include:

  • Product Vision
  • Task Board
  • Design Models
  • Burn down
  • Burn up
  • Team Norms

No reason we should be looking at things like burndown during the standups

When co-located, things like standups twice a day might make sense

Velocity

Usually takes 3-4 sprints to get an established velocity.

Calculating: total points done / number of sprint = velocity

Can be used to calculate how much longer project will take

Risk

Can have brainstorming meeting about risk:

Goal to just ID possible threats

Assess how likely to happen, and how bad it will be if it does

 Should be considering risks in planning meetings

Risk Register

  • Owner of Risk
  • Mitigation Plan
  • Escalation Path – where does owner go if they need help

Kaizen

Agile is based on Kaizen (hey my mom did that in the 90s!) 

This means improving the quality of the work by making small adjustments over time

The Retrospective is a clear use of this idea. It is asked:

  • What went well?
  • What can be improved?
  • What can be done differently to improve? 

Growing Skills

  • Pair Programming
  • Skill Shares
  • Encourage continuous learning

Lean

A way to identify and eliminate a system’s waste without affecting productivity.  

Adapted from manufacturing.

  • Partially done work
  • Extra processes
  • Extra features
  • Task Switching
  • Waiting
  • Motion
  • Defects

Value Stream – The path a new idea has to go through to get to the customer

This should be mapped out, and you can then see where you can make it better

You can map any processes Value Stream –  like deployment process, and see where the problems are.

Is React’s setState Asynchronous?

For this post I am talking about in React when you use (see: https://react.dev/reference/react/useState):

const [foo, setFoo] = useState(“bar”);

And then you use:

setFoo(“fizz”)  ← This right here

Let me ask you a question:

Is setState Asynchronous?

  1. Yes!
  2. No!

Trick question, the answer is “Well, it’s really complicated, and you should just assume it is.”

However…If you want more information about it, I can give you an answer in several parts…It’s complicated.

I feel like the true answer depends on the exact definition of “asynchronous.” So let’s explore how `setState` actually works.

Going deeper:

setState executes in two parts, the first part executing immediately, the second part executing later, however that second part is immediately added to the event loop…Actually that second part isn’t totally true.  It’s not technically added to the event loop – it tells React that it needs to re-render, the check for that is already on the event loop in React’s internal event loop…So technically, it’s not added to the end of the event loop, basically a flag is set saying “do a re-render” and that re-render happens after the current render finishes, when React gets to that check in its internal event loop.

We need to be clear about two different parts of state in React for this to make sense.  

  1. React maintains an internal state that is (usually – it is exposed in updater functions…for brevity, I’m stoping here) not exposed to you, the developer.  
  2. There is also a component state in the example above, this is  foo.

These two states are sometimes the same, and they are sometimes different.

The first thing it does is execute an update to React’s internal state (#1 above).  That is totally immediate, and I don’t think anyone would argue that it is asynchronous.  

When React’s internal state is updated, React tells itself  “I need to do a re-render.”  This re-render however is deferred till after the current render cycle is completed.

This means that the state inside your component (in the example at the top foo, #2 above) is NOT updated till that re-render.  The state inside your component only ever changes during render.  This is true for anything involving state in your component.  More simply: component state only ever changes during the render cycle.

So, is that second part asynchronous?

Well, you can’t await is, so no, it’s not, end of story…Except you can’t await setTimeout and I think we generally agree that setTimeout is asynchronous…You can however wrap setTimeout in a Promise and you can await that…Turns out, you can also wrap a setState in a promise and await that…But don’t ever do that because it makes React unhappy and throw errors.

Fact: React will always execute setStates in the same order, so the last one will always be the final value.  

Fact: You need to use an updater function if you want to access the current internal React value (#1 above) – meaning, if the current render has updated the state, the only way to see that updated state is in an updater function. 

Fact: You CANNOT access the current internal React value of a DIFFERENT state value (during the current render cycle) in your component, even in an updater function.  Meaning, if you have two state values, and you update one, then you update the second one – with or without an updater function – you will ALWAYS get the un-updated value of the first one.  Why: Because component state (#2 above) only changes on the re-render, and that doesn’t happen till after the current render completes.

By “asynchronous” do we mean “doesn’t execute immediately?”  Do we mean “Is added to the microtask queue?” …Does setTimeout(..., 0) count as asynchronous? A lot of what I read says “does not hold up execution” which well, it doesn’t, except it does after other stuff…

Well, that lead me to reading the ECMAScript spec about setTimeout and I couldn’t discern if setTimeout(..., 0) is added to the event loop, added to the microtask queue, one of the previous but with a slight delay, or something else…I’m actually not sure that the behavior is defined – If someone smarter than me knows the answer to this please let me know.

What I do know is that a setTimeout(...,0) will always execute after the re-render cycle (I know this because it obviously isn’t part of React’s render cycle and always is the final update – in my testing) – meaning, that if you have a setTimeout(...,0) that sets the state, as well as other settings of the same state, the final value will always be the one set inside of the setTimeout(...,0) …Except that I say “always” and I actually don’t actually know if that is true.  It is true in my testing.  If that setTimeout is added to the microtask queue, in between other tasks that set that state, then it is possible that it won’t be the final value…but I don’t know if it is…but generally it is true – at least in my testing…And again, I’m not totally positive that is even defined in the spec…and we are splitting hairs here.

Because I don’t think that is complicated enough, React was kind enough to make running in dev mode as opposed to prod work differently.  Well, kind of.  If you are using an updater function, React will update twice in dev mode, and once in prod.  Why?  Oh god how deep does this hole go? 

Short answer: it should be a pure function. (see: https://en.wikipedia.org/wiki/Pure_function & https://react.dev/learn/keeping-components-pure)

Technically when React tells itself it needs to re-render, it applies the update to that component state var (#2) to it’s queue with the current internal value (#1) of the variable – meaning that changes to that variable inside of an updater function ARE NOT SEEN – as the original value was already applied, when the call was queued.  So if you update the state of the variable inside of an updater function, and then try to update it again later with an updater function, the first update is ignored.  Meaning: that’s a really bad idea.  So, in dev mode React will run it twice, once with the first value, once with the second value, and if they are different, ya done goofed.  The reason it does this in dev mode is to show you that you goofed.

So again, how is “asynchronous” technically defined?  And is it asynchronous?  IDFK.

I say setState is not asynchronous because the execution order is defined, and everything it does is immediately added to the the event loop when it is called – if you know what you are doing, the results are deterministic, you absolutely know what the final result will be.  I say please don’t ever rely on this, because the next person who has to modify the code – including future you – is generally not smart enough to understand the nuances here, and if your code relies on this behavior, they will likely break things.

I also say it is asynchronous because part of it executes out of order, and we can (in theory) use a promise to `await` it.

Additionally – because this behavior is so esoteric, I don’t know that it will not be changed in React, sometime the future.

ALL OF THIS IS WHY I SAY TO JUST TREAT IT AS ASYNCHRONOUS! 

I probably made some technical mistakes above…Though I do think it is basically correct.  What I wrote is based on my reading many things, watching many things, and a butt load of tests I wrote myself….Really, I should have saved those tests so I could post them…If you want me to reproduce and post those test, let me know.

PLEASE LET ME KNOW IF ANYTHING I WROTE ABOVE IS INACCURATE.

Some Thoughts on Copilot and AI for Software Development

I have been using Github Copilot for software development for the past year, and my company is now starting to use the corporate version, and I’m even getting Microsoft’s training.  Using AI for software development is clearly the biggest change to happen to the industry since the internet, and it is important to continually evaluate how useful and effective we find new tools to be.  My results range from as a friend described it “autocomplete for my brain” – to, actively making my job harder, my work slower, and (if not checked) having more bugs.

The benefits of Copilot (I will be using Copilot and general AI for software development rather synonymously, but not totally in this post…I assume you are an adult and can figure it out) are clear to many of us who have used it for any extended period of time.  I do not know if it reads my clipboard, or is just good at guessing, but its ability to know that I want to add a specific variable to a string, or in a log is uncanny. When debugging a problem I have even seen it know when I click on a specific line, exactly what I want to log before I do anything – then when I verify the problem is what I believed it to be, when I then click on a different line, it knows the correct fix.  I have really had some mind-blowing moments.

Another thing AI can be great for is refactoring.  A lot of refactoring isn’t “hard” so much as “long and tedious” where it is easy to miss something here or there and break functionality.  In my experience Copilot does a good job of helping refactor without missing things – something I have always struggled with when using say a VSCode extension for refactoring.  I fully admit my experience using extensions is limited as every time I have tried to use them, I get frustrated and just say “fuck it, I’ll do it myself!”

An additional area where I see a benefit using AI tools is when there is something I don’t quite know how to do.  For instance, when I want to unit test a piece of code, but I am not sure how to build the scaffolding needed for the unit test to function – literally the worst part of writing code – many times Copilot can help at least get me started down the right path.

This however leads us directly into my first major problem with using AI.  When we talk about unit testing, AI really wants to help you do it.  It can be great for quickly generating scaffolding.  The problem arises with what checks the tests actually perform.  When you ask AI to unit test code for you, it usually just tests that the code does what you programmed it to do.  This isn’t just unhelpful, it is actively harmful.  Much of the point of writing tests is to catch problems, if the test simply checks that the code does what you programmed it to do, that is a bad test.  We need tests that catch the mistakes in what we programmed.  Otherwise we have a not just a false sense of security but an actively bad sense of security.

Following that problem is the one I have with AI writing code for me.  When I write code, I write code to do something, and I know what I intended it to do because I wrote it to do that. Many times AI wants to take the wheel and complete the logic for me.  Though this is nice in theory, reading and understanding code you didn’t write takes longer, and is harder than just writing it yourself.  I have had experiences where Copilot has written 3-4 lines, or even an entire block of logic, and the amount of time it takes me to analyze it to verify that it is doing what I want, and doing it correctly, actually takes longer than just writing it myself.  Basically it is like I have to review a PR – the other worst part of development.  And yet, we all look to StackOverflow…And some of us looked to help files and books before the internet existed. Sometimes it can be useful in that it might show me a path I may have forgotten, still many times it is slightly not quite correct, or even if it is, it was harder, and took longer for me to use its code.  If it is incorrect…How many developers are actually taking the time to analyze this code correctly?  Well, how many currently do it in PRs?  YMMV.

I have also found code-reuse to be another issue with AI generated code.  Though I don’t believe that every line has to be as DRY as possible, I do believe that code reuse is important.  Moving functionality that does the same thing to a single location helps prevent bugs because the “same” logic isn’t rewritten multiple times, it also makes updating logic easier because it only exists in one place.  I have found that AI isn’t super helpful for this.  It doesn’t suggest I refactor out code from before because I am doing the same thing here.  It doesn’t suggest I build some kind of factory instead of generating the same objects over and over.  It doesn’t know or care that I’m doing this.

The last area I have found AI to be a large problem is learning new things.  And yet, I do think it can learn these these skills. It can – possibly – be an amazing instructor.

I recently decided to learn some Rust for fun.  After about an hour of work I found I had to completely turn off Copilot.  Having autocomplete try to help me with everything when I know nothing just means I learn nothing.  I keep hitting autocomplete and nothing ever cements in my brain.  What can be helpful is when I get stuck, using the Copilot chat to try to help.  This way it never tries to help me until I ask it to. I can ask for help on how to do something new – help is never bad. Blindly listening is totally unhelpful.

In general I have found AI to be helpful in MY workflow – I have reservations about other developers, most especially junior developers using it.  I worry that people will not fully vet the code that it gives them.  I worry that people will generate tests that make them believe their code is safer than it is.  I worry that people won’t actually LEARN.  That said – did we not have the same concerns with things like StackOverflow?  I think we can all admit SA has become less useful over the last 5 to 10 years.  I rarely even care about results that are more than a year old.  Issue trackers, forums, and conferences are much better sources of information.  Perhaps AI is what is needed to replace legacy tools like SA.  What I do know for sure is that if my 8-year-old self had something like Copilot when I first started coding, I would have loved it, just as I loved the QBasic help files when I was that kid. 

Oops…

Well, I hit the “update” button in WordPress and it hosed my site.

It took a lot of fighting to get it working again.

My content is – mostly restored, but there are still issues with old posts and code and things.

I spent a while trying to make it useable – but most of the code stuff is jQuery, and I haven’t written jQuery in nearly a decade, so I don’t really have any plans to go back and fix the problems any more than I already have. It’s already eaten too much of my time.

Bookmarklet For Increasing YouTube Speed

I made a new tiny bookmarklet to let you increase the speed of a YouTube video beyond 2x.

As always, just drag this link to the bookmark bar on your browser, then just start a YouTube video, once the video starts, click the bookmark, and enter how many “x” you want the video played at. I usually stick to 2.5 or possibly 3, it all depends on the video – but you do you!

YouTube Speed

The code is pretty simple and easily found online, but just in case that’s what you are looking for:

(function(){document.getElementsByTagName('video')[0].playbackRate = window.prompt('Please enter a new speed:');})();

Facebag – A Chrome Extension to make Facebook less interactive!

Long time no update! Well, I just made myself a little Chrome Extension for Facebook. All it does is delete the comment boxes, status box and like buttons from Facebook, so that I don’t use them. It’s great for me! I am considering adding other things to it as well, but for now this was quick and dirty and did what I wanted!

Chrome Store Link

Github Repo

Netflix Autoplay Chrome Extension – v0.8 – Chromecast Support!

I put out an update the other night, this one adds support for Chromecast.

And it looks like I was just in time for the new season of Orange Is the New Black, so get your binge on!

Web Store: https://chrome.google.com/webstore/detail/netflix-autoplayer-with-s/adegickccmlojbmbmemgjakpoammfmkg?utm_source=chrome-ntp-icon

Github: https://github.com/rtpmatt/Netflix-Autoplay-Chrome-Extension

Netflix Autoplay Chrome Extension – Still More Updates

I have added still more updated to my Chrome extension.

0.6 Updates:
*Added episode counter in addition to timer
*Pausing video stops timer
*Fixed bugs when stopping

0.5 Updates:
*Play/Pause, Next and Stop media buttons on keyboard now work with Netflix.
*ctrl+q now causes the same action as the sleep timer ending (ex: pause movie & sleep).

Chrome store link: https://chrome.google.com/webstore/detail/netflix-autoplayer-with-s/adegickccmlojbmbmemgjakpoammfmkg?utm_source=chrome-ntp-icon

Github link: https://github.com/rtpmatt/Netflix-Autoplay-Chrome-Extension

Netflix Autoplay Chrome Extension with Computer Sleep / Shutdown – Updates

I have added a number of updates to my Netflix autoplay extension. In addition to autoplaying TV episodes and shutting down when done, it now also adds support for the keyboard media keys play/pause, next, and stop to Netflix, and also adds a new keyboard shortcut of ctrl+q which does the same thing that will happen when the timer reaches zero. The extension and Github have both been updated.

Also, a little while ago I added default value options for the popup that can be set in the extension option.

Github: https://github.com/rtpmatt/Netflix-Autoplay-Chrome-Extension

Chrome Store Link: https://chrome.google.com/webstore/detail/netflix-autoplayer-with-s/adegickccmlojbmbmemgjakpoammfmkg?utm_source=chrome-ntp-icon

Netflix Autoplay Chrome Extension – With System Sleep!

Bookmarklets are cool and all that, but what I have really always wanted the autoplay functionality for Netflix to do is emulate the ‘Sleep Timer’ found on most TVs. That is, I want it to play for some amount of them, and when done, shut everything off. I know I am not the only person around who has used the function on the TV for years while going to sleep. I have known that this is not possible using just a bookmarklet, but I never got the energy up to actually figure out how to make one. One of the reasons is that I have always known that calling something external like the Shutdown/Sleep command on the computer would be a huge pain. This new HTML5 video Netflix is using for Chrome though made it just a bit too tempting.

The core functionality works basically the same way as this one, since Chrome extensions just use Javascript. I had to figure out how to do all the fancy stuff that makes it an extension, AND add the stuff to allow it to interact with the system to call the Sleep command.

And here it is: Netflix Autoplayer – Chrome Extension

IMPORTANT:
For the ‘Sleep’ functionality to work, once you have installed the extension, you must download the install.bat file that you will see a link for at the bottom of the dialog for the extension. You must run this (and I believe you might need administrator privileges when you do) it creates a couple files that are needed and adds a registry entry. Really, in general I would say you should not do something like that. You shouldn’t be running random things people on the internet tell you to. If you want the ‘Sleep’ to work though, that’s what you need to do. If you understand how .bat files work, it is actually really small, so you can check out what it is doing.

Once you have done the above you can actually edit one of the files it creates, it is located at:
%LOCALAPPDATA%\Netflix-Autoplay\shutdown.bat

And by default should have the following:

:: Lock
::rundll32.exe User32.dll,LockWorkStation
:: Shutdown
::Shutdown.exe -s -t 00
:: Hibernate
::rundll32.exe PowrProf.dll,SetSuspendState
:: Sleep
rundll32.exe powrprof.dll,SetSuspendState 0,1,0

As you can see, ‘Sleep’ is what it does by default, but I have entries for Hibernate, Shutdown, and Lock all listed, just uncomment the one you want and comment the others back out…Hell if you want you could really put anything you like in that file and make it run anything when the timer gets to zero.

Chrome Web Store Link: https://chrome.google.com/webstore/detail/netflix-autoplayer/adegickccmlojbmbmemgjakpoammfmkg?utm_source=chrome-ntp-icon

Github Link: https://github.com/rtpmatt/Netflix-Autoplay-Chrome-Extension

Hope you enjoy!

NEW Netflix Autoplay Bookmarklet!

GOOD NEWS EVERYONE!

I was checking out Netflix today, and they seem to have moved to an HTML5 video player (for me in Chrome at least, and that is all I tested). This is awesome news as it made writing a new autoplay bookmarklet easy as shit! They also got rid if the bit of Javascript they were trying to use to prevent you from using the console. I don’t know why they made the decision to get rid of that, it could be because they know it is a dumb idea and anybody who actually knows what they are doing can get around it, it could be because they are now OK with people playing around, it could be just an accident, but I am going to take personal credit for it because of this post: I Don’t Like Being Told What I Can and Can’t Do.

Anyway, as I said, this awesome new HTML5 video player made my job super easy – as you can see by how much less code there is this time…and here it is:



‘use strict’;
(function(undefined){
//Check if user has already loaded an instance – if so, just update the play time;
if(window._ME && window._ME.autoplayer) {
//Get desired play time extension & convert to miliseconds
window._ME.autoplayer.playTime = window.prompt(‘Autoplay already started! Updating playtime. \n How many more minutes would you like to play for?’) * 60 * 1000;
if(isNaN(window._ME.autoplayer.playTime)) {
window.alert(‘That\’s not a number jackass.’);
}
window._ME.autoplayer.startTime = new Date();
return;
}

window._ME = {
autoplayer: {}
};

//Get desired play time & convert to miliseconds
window._ME.autoplayer.playTime = window.prompt(‘How many minutes would you like to play for?’) * 60 * 1000;
if(isNaN(window._ME.autoplayer.playTime)) {
window.alert(‘That\’s not a number jackass.’);
}
window._ME.autoplayer.startTime = new Date();
var lastUpdate = new Date();

//Checks if the video has stopped or if we are done playing once a second
window._ME.autoplayer.interval = setInterval(function() {
var currentTime = new Date();

// Check if autoplay-interrupt has fired
if(document.getElementsByClassName(‘player-autoplay-interrupter’).length > 0 && document.getElementsByClassName(‘continue-playing’).length > 0) {
//Just click the continue button!
document.getElementsByClassName(‘continue-playing’)[0].click();
}

//Check if at end of season
if (document.getElementsByClassName(‘player-postplay-autoplay-header’) && document.getElementsByTagName(‘video’).length === 0 && document.getElementsByClassName(‘player-postplay-still-hover’).length > 0) {
//Click the next video picture
document.getElementsByClassName(‘player-postplay-still-hover’)[0].click();
}

//Check if we have reached the users max play time
if(window._ME.autoplayer.playTime && currentTime – window._ME.autoplayer.startTime > window._ME.autoplayer.playTime && document.getElementsByClassName(‘player-play-pause’).length > 0) {
//click the pause button
document.getElementsByClassName(‘player-play-pause’)[0].click();

//remove all traces of this autoplayer
clearInterval(window._ME.autoplayer.interval);
delete(window._ME);
}

lastUpdate = currentTime;
}, 1000);
})();
[/sourcecode]

Basically all I had to do this time was watch for when the right elements popup on the screen, and when they do, click them. Thank you Netflix!

The Thing You Want:
[raw]
Here is the Bookmarklet: Restart Timer
[/raw]

Turn off Autoplay:
[sourcecode language=’javascript’]
(function() {
clearInterval(window._ME.autoplayer.interval);
delete(window._ME);
})();

[raw]
Bookmarklet: Turn off Autoplay
[/raw]

If you are new to this, to use these, simply drag those links up to your bookmark bar, then start up the TV show you want to watch, and click the first bookmark.

The bookmarklet will ask you how long you want to play for, and it will pause the video and remove itself when it hits that time limit. For never-ending play, just enter “0”.
-If you hit it a second time, it will as you again how long you want to play for, and restart the timer with the new length.

If you hit the second bookmarklet I have there, it will restart the timer. Meaning that if you put in “60” (for 1 hour of play time) hitting it will restart the countdown back to 60 minutes.

If you hit the third bookmarklet, it will remove all traces of the bookmarklet from the page (basically the same as just hitting refresh on the page).

This should have no problem with multiple seasons, or any of that fancy stuff. Let me know if you find any bugs, I may do something about them I guess.

And I should have this up on GitHub shortly here: https://github.com/rtpmatt/Netflix-HTML5-Autoplay-Bookmarklet

Update:
Yeah, I just tried this in FireFox, and apparently it still uses SilverLight, not HTML5 videos, so this wont work at all in it. So, please don’t tell me about how it is broken in FireFox, I will not be doing anything about that…If you are using IE, then I REALLLY don’t care if it works or not. If you send me anything about your IE situation I may send you back an insult, or possibly a picture of a cat butt, that might be fun for you I guess? I don’t know if you will be able to view them on IE though.

Checking for global scope leak

Last night I got a wild hair and wanted to write a bit of code for fun. Basically I wanted to see what variables were added to the global scope by different libs and bits of code. Basically you can run this and it will tell you if you have accidentally left off a var or added something to the global namspace in some other way. It is rather simple, it does add a var to the global, but I remove that at the end so you don’t see funny results.

To run it, simply replace the:



with whichever files you wish to check. The code is pretty simple:














I Don’t Like Being Told What I Can and Can’t Do

I was trying to update my Netflix autoplay bookmarklet since the autoplay feature that Netflix came out with is…not great. What I found was that Netflix tried to disable the console. Well, that became a more interesting problem to solve…and quite an easy one too. The code Netflix uses to disable the console is:

(function () {
    try {
        var $_console$$ = console;
        Object.defineProperty(window, "console", {
            get: function () {
                if ($_console$$._commandLineAPI) throw "Sorry, for security reasons, the script console is deactivated on netflix.com";
                return $_console$$
            },
            set: function ($val$$) {
                $_console$$ = $val$$
            }
        })
    } catch ($ignore$$) {}
})();

To undo this is actually really simple, we just need to set the getter/setter back to 'undefined' and everything is happy:

Object.defineProperty(window, "console", {
	get: undefined,
	set: undefined
});

And of course this is easy to make into a bookmarklet like so:

Enable Console
Sorry Netflix =)

Netflix added autoplay!

Hey I don’t need to update my bookmarklet any more. Actually, I was going to update it again, but I had an interview with them and they told me it was about to happen, so that was why I didn’t bother! Didn’t know if I should post that before it happened though. It was fun though, thanks everybody for enjoying it!

Fixed Netflix Autoplay Bookmarklet!

As promised, here is the new fixed version of my bookmarklet:

Netflix Autoplay Bookmarklet
Please note I can no longer tell when the episode is almost over, so I am now just timing the entire length of play. You will now see a countdown telling you when it will change to the next episode. This means a few things:
* If you pause the show, or it takes a long time to buffer, or is has to buffer in the middle, the timer will be off and it will switch early. There are some things you can do about this, first, you will see ‘||’ next to the timer, clicking it will pause the countdown, clicking again will unpause it, so if you walk away, be sure to click this. Also, you can click the countdown and type a new time into it, you can either give a number of seconds, or enter time in the mm:ss format.

Also, next to the pause button you will see a ‘-‘ most of you can ignore this, but if you have a lot of problems with buffering taking a really long time or something, you can also click the ‘-‘ and enter a number of seconds there. This will be added to what the bookmarklet thinks is the length of each episode to allow for buffering and such. By default 10 seconds is added to allow the episodes to load properly.

I have not done extensive testing on this, so there could very will be some bugs. If you find any, let me know.


(function(undefined){

//Takes our countdown timer and converts it to mm:ss and displays to user
function updateTime() {
var seconds = (countdownTimer % 60) + '';
if(seconds.length === 0)
seconds = '00';
else if(seconds.length === 1)
seconds = '0' + seconds;

timerNode.innerHTML = Math.floor(countdownTimer / 60) + ':' + seconds;
}

//Grabs the data for the episode matching the ID passed in
function getCurrentEpisodeData(episodeId) {
var episode, i = 0, j = 0;

if(episodeData && episodeData.video && episodeData.video.seasons) {
for(i=0; i<episodeData.video.seasons.length; i++) { for(j=0; j<episodeData.video.seasons[i].episodes.length; j++) { if(episodeData.video.seasons[i].episodes[j].id === episodeId) { episode = episodeData.video.seasons[i].episodes[j]; break; } } if(episode) break; } } return episode; } //Grabs the data for the episode following the one with the ID passed in function getNextEpisodeData(episodeId) { var episode, i = 0, j = 0, found = false; if(episodeData && episodeData.video && episodeData.video.seasons) { for(i=0; i<episodeData.video.seasons.length; i++) { for(j=0; j<episodeData.video.seasons[i].episodes.length; j++) { if(episodeData.video.seasons[i].episodes[j].id === episodeId) { found = true; } else if(found) { episode = episodeData.video.seasons[i].episodes[j]; break; } } if(episode) break; } } return episode; } //base64 decoder function decode64(input) { var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', output = '', chr1, chr2, chr3 = '', enc1, enc2, enc3, enc4 = '', i = 0, base64test = /[^A-Za-z0-9\+\/\=]/g; input = input.replace(base64test, ''); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } chr1 = chr2 = chr3 = ''; enc1 = enc2 = enc3 = enc4 = ''; } while (i<input.length); return unescape(output); } if(window.NetflixAutoplayLoaded) { alert('Autoplay already loaded.'); //return false; } else { window.NetflixAutoplayLoaded = true; } //resize the player so we can see the text we are about to insert under it document.getElementById('SLPlayer').style.height = (window.innerHeight - 35) + 'px'; document.getElementById('SLPlayerWrapper').style.height = (window.innerHeight - 35) + 'px'; document.getElementById('page-content').style.height = (window.innerHeight - 35) + 'px'; //create the text and other shizzy we want var autoplayElement = document.body.appendChild(document.createElement('div')); autoplayElement.id = 'NetflixAutoplayContainer'; autoplayElement.innerHTML = '

Time left until switch: 00:00 || -
'; var timerNode = document.getElementById('NetflixAutoplayTimer'); var autoplayText = document.getElementById('NetflixAutoplay'); var pauseButton = document.getElementById('NetflixAutoPauser'); var delay = document.getElementById('NetflixAutoDelay'); //the number of seconds to wait for the show to buffer on load var timerDelay = 10; //Pull the current episode information and the full series data for us var episodeData = JSON.parse(decode64(netflix.Silverlight.MoviePlayer.getPlugin().settings.metadata)); var currentEppId = (/,EpisodeMovieId=\d*/.exec(netflix.Silverlight.MoviePlayer.getPlugin().settings.initParams)[0]).split('=')[1]; //gets data for current & next epps var currentEpp = getCurrentEpisodeData(currentEppId); var nextEpp = getNextEpisodeData(currentEpp.id); var paused = false; //bool to see if paused var done = false; //bool to see if finished autoplay var editingTime = false; //bool to check if user is editing time //Javascript to execute to change episode var ini = document.getElementsByTagName('script'); ini = ini[ini.length-1].innerHTML; //Set the countdown till next episode + the pause we need for buffer function updateCountdown() { countdownTimer = parseInt(currentEpp.runtime, 10) + timerDelay; } var countdownTimer = 0; //episode length timer updateCountdown(); //Sets the count down timer for current episode //Prompt user for number of episodes var numToWatch = 3; function getNumberOfEpisodesToWatch() { var newNum; do { newNum = prompt('How many episodes would you like to play?', numToWatch); } while (isNaN(newNum)); numToWatch = parseInt(newNum, 10); //set the text if(numToWatch > 0) { autoplayText.innerHTML = 'Netflix autoplay on, Episodes left: ' + numToWatch; if(done) { //if we have already finished, restart done = false; switchEpps(); } } else { autoplayText.innerHTML = 'Netflix autoplay off'; } } //ask the user for the number of episodes they want getNumberOfEpisodesToWatch(); //handler for pause button function pause() { if(pauseButton.innerHTML === '||') { paused = true; pauseButton.innerHTML = '>'; } else { paused = false; pauseButton.innerHTML = '||'; } } //handles editing the buffer delay function delayEdit(e) { if((!e.keyCode || e.keyCode === 13) && !isNaN(delay.innerHTML)) { timerDelay = parseInt(delay.innerHTML, 10); if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return false; } } //handler for editing time left to play function editTime(e) { timerNode.contentEditable=true; editingTime = true; if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return false; } //handles updating time left to play after user edit function endEdit(e) { if(timerNode.contentEditable.toString() === 'true' && (e.type === 'blur' || e.keyCode === 13)) { timerNode.contentEditable = false; editingTime = false; var time = timerNode.innerHTML; if(time.indexOf(':') >0) { //converts minutes:seconds into time time = time.split(':'); countdownTimer = parseInt(time[0], 10)*60 + parseInt(time[1], 10); } else { //converts just seconds into time if(!isNaN(time)) { countdownTimer = parseInt(time, 10); } } if (!e) var e = window.event; e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return false; } } //Does everything to change to the next episode function switchEpps() { //setup the script for the next epp ini = ini.replace(/,EpisodeMovieId=\d*/,',EpisodeMovieId=' + nextEpp.id); //switch out data to the new epps currentEpp = nextEpp; nextEpp = getNextEpisodeData(currentEpp.id); //Switch to next epp & update text if(currentEpp) { autoplayText.innerHTML = 'Netflix autoplay on, Episodes left: ' + numToWatch; updateCountdown(); eval(ini); nextEppTimer(); } else { autoplayText.innerHTML = 'There does not seem to be a next episode.'; } } //Main loop, updates our timer and stuff, switches to next epp when necessary, yadayda function nextEppTimer() { setTimeout(function() { if(isNaN(countdownTimer) || isNaN(numToWatch)) { autoplayText.innerHTML = 'NUMBERS DO NOT WORK THAT WAY! GOOD NIGHT!'; nextEppTimer(); return; } if(countdownTimer <= 0) { if(numToWatch-- > 0) { switchEpps(); } else { done = true; autoplayText.innerHTML = 'Netflix autoplay completed.'; numToWatch = 0; } } else { if(!editingTime && !paused) { countdownTimer--; updateTime(); } nextEppTimer(); } }, 1000); } //attach the events we need autoplayText.addEventListener('click', getNumberOfEpisodesToWatch, false); pauseButton.addEventListener('click', pause, false); timerNode.addEventListener('click', editTime, false); timerNode.addEventListener('blur', endEdit, false); timerNode.addEventListener('keypress', endEdit, false); delay.addEventListener('blur', delayEdit, false); delay.addEventListener('keyup', delayEdit, false); //START EVERYTHING! nextEppTimer(); })();

I Know My Netflix Bookmarklet Broken!

Yes folks, I know. I am sorry, the new player they released last week broke it. I too was sad to discover this, I mean I didn’t really make it for you all, I made it because I wanted it! I really didn’t know if anybody was actually using it, but seeings as my site has maintained traffic at pretty much double by highest day ever EVERY DAY since Netflix broke it, I guess other people like it too. Netflix changed a lot of things, some of it easy to fix, some not as much. Shit happens; que sera sera.

YES, I HAVE A FIX! It is not done yet. I think it will be better in some ways, worse in others. I promise you all (baring catastrophe) it will be out this week.

Thanks to everybody leaving comments and sending me emails telling me that you love it, were upset that it broke, and really want me to fix it! It is amazing to have people actually getting enough enjoyment out of something I made to do those things!

TL;DR: Fix will be out this week.

Getting All jQuery Events for a DOM Node.

I have more than once found myself needing to know all of the events for a specific DOM node. In the past I have used which worked great for a time. The problem is that I am now working on projects so large that Visual Events takes forever to load and then fills the entire screen making it nigh impossible to find the events for the node I am interested in. Combine this with live events, delegated events, on/off, etc, Visual Events was no longer doing the job for me. So I made myself another bookmarklet.

jQuery Events

To use it:

Drag the link to your bookmark bar.

Open the page you wish to see the events on.

Open your javascript console.

Click the bookmarklet.

Click the little red “Click Me” it puts in the top left corner.

Click the element you wish to see the events for.

In your console you will see a list of all of the events for the node you clicked, what type they are, and their namespace. If the node you click has no events (perhaps you clicked an element inside of the one with the events) it will look up the DOM until it finds a node with events. It will also look all the way up the DOM for that node and show you all of the events that are delegated to that node. The code could use a lot of cleanup and stuff, but it works for me right now.

Here is the code for it:


javascript:(function() {
$('#eventFinder, #eventTitle').remove();

$(document.body).append('




');

$(document.body).append('

Click Me
'); $('#eventTitle').click(function() { $('#eventFinder').show(); }); function findNodeEvents(e) { $('#eventFinder').hide(); var ele; if(e.pageX) { ele = document.elementFromPoint(e.pageX, e.pageY); } else { ele = e[0]; } var tmpEvents; var $origEle = $(ele); var $ele = $origEle; var events = []; var eventsFound = false; console.log('Finding events for node:'); console.log($origEle); while(true) { tmpEvents = $ele.data('events'); if(tmpEvents) { for(type in tmpEvents) { for(var i = 0; i < tmpEvents[type].length; i++) { if(((!tmpEvents[type][i].selector && $ele[0] === ele) || $origEle.is(tmpEvents[type][i].selector))) { eventsFound = true; if($ele[0] !== ele) { console.log('Delegated From:'); console.log($ele); } console.log('Event: ' + type + (tmpEvents[type][i].namespace ? '.' + tmpEvents[type][i].namespace : '') + ': ' + tmpEvents[type][i].handler); } } } } $ele = $ele.parent(); if($ele.length <= 0) { break; } } if(!eventsFound) { var $parent = $origEle.parent(); if($parent.length > 0) { console.log('No events found - Trying parent'); findNodeEvents($parent); } else { console.log('No events found - Do you know what you are doing?'); } } }; $('#eventFinder').click(findNodeEvents); })();

I Githubed myself!

(Githubbed?) Well, I finally took the time to throw my bookmarklet up on Github, I have some more projects I may add soon, but for now, if you want to fork my bookmarklet, I have it here:

https://github.com/rtpmatt/Netflix-Autoplay-Bookmarklet

Also, getting on Github with windows was not as easy as they made it sound, I had to generate keys using puttygen, doing it through GIT bash did not work at all like Ghithub’s documentation lead me to believe.

Fixing the Terrible: Turning an SVN Branch into a GIT Repository

Long ago, someone, for some reason, thought it was a good idea to use branches in SVN as if they were repositories, so one repository contained many different branches each branch being its own completely independent project. When we moved to GIT we agreed to not repeat this mistake. We converted the entire SVN repo to a GIT repo long ago using Svn2Git, this worked well, except we still had all of those crazy branches in there. I don’t remember how we got the other branches out originally, but I am sure it was not the best way…or even a good way. We have been using GIT for some time and understand it better, and we still have a few old branches that we never extracted, but the time has come for us to do so. It was much easier now that we have some clue what is going on, and I though I would explain how we did it because it is rather simple…if you know what you are doing.

Overview:
For us we actually want these to be bare remote repositories can be pushed to. Basically all you need to do is push the single branch into a new GIT repository and create a “master” branch in it. If you are making a bare repository, you then just need to clone the new repository to a bare one. Really, that is it, so here is a breakdown.

Breakdown:
So, working on the assumption that we have our entire SVN repo (or whatever is your poison) converted to a GIT repo, we start by creating a new empty repo. Create a new directory to hold the repo, then go into that directory and run:

git init

This creates a new empty repository.

Now, push the branch from the old repo into the new one by going into the directory of the old repo and running:

git push /path/to/new/repo branch-to-extract

At this point your new repo contains only the single branch you extracted!

Next we have to create a new “master” branch for this repository. To do so, first we have to checkout the old branch. Navigate to the directory for the new repo you just created and run:

git checkout branch-to-extract

You should now see the code that was in that branch. Now we create a new “master” branch with:

git branch master

At this point you have a GIT repo for you project that is good to go! If this is all you need, you are done! If you want to turn this into a bare remote repository that someone can use, simply clone it –bare as normal.

Simply clone this new repo to the new remote one:

git clone --bare /path/to/new/repo /path/to/bare/repo

You can now clone this repo to your local machine and use it normally, with all the great pushing, pulling, branching and everything else GIT has to offer. If you like you can delete the “/path/to/new/repo” repo that we setup to extract old branch from, or delete the branch that you imported from from your fantastic new repo.

It really is that easy. It all makes perfect sense now and took me only a few minutes to figure out. When we were first trying to move to GIT though, and didn’t really understand it, this was not easy and did not make perfect sense. Hopefully if someone else out there has done the same bad thing or something similar and is now trying to fix the problem this will help them out.

Netflix Autoplay Bookmarklet – With Multiple Season Support!

Not content to leave well enough alone, I was hacking on my bookmarklet some more. It can now handle crossing between seasons. There is also a bit less guessing going on now, so I am more confident in it.

For the record, I want to say, I hope Netflix does not get upset about this, if they want me to remove it I will.

To use this:
1. Drag the link below on to your bookmarks bar.
2. Start watching a TV show on Netflix.
3. Click the bookmark and enter then number of episodes you want to watch.

Note: I have now tested this in IE. It does NOT work. But you wouldn’t use IE anyway, would you?

Netflix Autoplay Bookmarklet – With Multiple Season Support![/raw]


javascript:(function(netflix, undefined) {
var seasonId = 0,
episodeId = 0,
numWatched = 0,
numToWatch = 3,
epIdRegex = /,EpisodeMovieId=\d*,/,
idregx = /\d+/,
done = false,
sl,
init,
currrentEpisodeId,
currentMovieId,
seasons,
showData,
waitTimer,
node;

if(!netflix || !netflix.Silverlight || !netflix.Silverlight.MoviePlayer || !netflix.Silverlight.MoviePlayer.getPlugin() || !netflix.Silverlight.MoviePlayer.getPlugin().settings.initParams) {
alert('You do not appear to have a show playing, please start a show first');
return;
}

//grab the things we need
sl = netflix.Silverlight.MoviePlayer.getPlugin().getScriptInterface();
init = netflix.Silverlight.MoviePlayer.getPlugin().settings.initParams;
currrentEpisodeId = parseInt(idregx.exec(epIdRegex.exec(init)), 10);
currentMovieId = parseInt(netflix.Silverlight.MoviePlayer.getPlugin().settings.movieId, 10);

//Check if the user has already loded teh bookmarklet
var autoplayElement = document.getElementById('NetflixAutoplay');
if(autoplayElement) {
alert('You have already loaded the autoplay bookmarklet, click the text at the botton to change number of episodes.');
return;
}

//grab the metadata and decode it
try {
showData = JSON.parse(decode64(netflix.Silverlight.MoviePlayer.getPlugin().settings.metadata));
} catch(e) {
alert('Error processing data =(');
return;
}

if(showData.Movie) {
alert('This appears to be a movie not a TV show. This bookmarklet only works on TV show.');
return;
}

//set our pointest to match the episode we are currently on
seasons = showData.Show.Seasons;
for(seasonId = 0; seasonId < seasons.length; seasonId++) { for(episodeId = 0; episodeId < seasons[seasonId].Episodes.length; episodeId++) { if(seasons[seasonId].Episodes[episodeId].MovieId === currentMovieId || seasons[seasonId].Episodes[episodeId].MovieId === currrentEpisodeId) { done = true; break; } } if(done) { break; } } //check if we were able to find the episode the user is on if(seasonId === seasons.length) { alert('Error: Already of final episode, or episode data could not be found.'); return; } //Prompt user for number of episodes function getNumberOfEpisodesToWatch() { var newNum; do { newNum = prompt('How many episodes would you like to play?', (numToWatch - numWatched)); } while (isNaN(newNum)); numWatched = 0; numToWatch = parseInt(newNum, 10); //set the text if(numToWatch > 0) {
autoplayElement.innerHTML = 'Netflix autoplay on, Episodes left: ' + numToWatch;
} else {
autoplayElement.innerHTML = 'Netflix autoplay off';
}
}

//create the text that shows how many episodes left & insert it
node = document.createElement('span');
autoplayElement = document.body.appendChild(node);
autoplayElement.id = 'NetflixAutoplay';
autoplayElement.innerHTML = 'Netflix autoplay on, Episodes left: ' + numToWatch;

//attach a click handler so people can change number of episodes
autoplayElement.addEventListener('click', getNumberOfEpisodesToWatch, false);

//prompt the user for number of episodes for the first time
getNumberOfEpisodesToWatch();

//handle when the episode ends
sl.OnMovieWatched = function() {
if(numWatched < numToWatch && !waitTimer) { //Check if done autoplaying waitTimer = setTimeout(function() { //Set our timer so we do not end early var epp, numLeft; //move episode/season counters properly if(seasons[seasonId].Episodes[episodeId+1]) { episodeId++; } else { episodeId = 0; seasonId++; } //if there is a next episode, grab it if(seasons[seasonId] && seasons[seasonId].Episodes[episodeId]) { epp = seasons[seasonId].Episodes[episodeId]; } //if there is a next episode, play it and update the text if (epp) { sl.PlayMovie({movieId: epp.MovieId, episodeMovieId: 0, trackId: 0}); numWatched++; numLeft = numToWatch - numWatched; if(numLeft > 0) {
autoplayElement.innerHTML = 'Netflix autoplay on, Episodes left: ' + numLeft;
} else {
autoplayElement.innerHTML = 'Netflix autoplay completed.';
}
}

//cleanup
clearTimeout(waitTimer);
waitTimer = null;
}, 2*60*1000);
}
};

//This is just a bse64 decoder
function decode64(input) {
var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
output = '',
chr1, chr2, chr3 = '',
enc1, enc2, enc3, enc4 = '',
i = 0,
base64test = /[^A-Za-z0-9\+\/\=]/g;

input = input.replace(base64test, '');

do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));

chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } chr1 = chr2 = chr3 = ''; enc1 = enc2 = enc3 = enc4 = ''; } while (i<input.length); return unescape(output); } })(window.netflix); 

In addition to handling moving between seasons, it now also adds a little text below the movie to tell you that it is on. You can click this text to change the number of episodes it will play. Entering 0 for the number of episodes will turn this off.

Netflix Autoplay Bookmarklet!

I have updated this plugin, please use the new BETTER version – Click Here

Netflix is awesome, most of the time the fact that I have to click the “Play next episode” button does not bother me, usually I am at my computer anyway. But, if you are like me, you enjoy throwing on a show (say Futurama) and having a few episodes play while you go to sleep. The ~22 minutes an episode is just not enough, I like to have about 3 play. Netflix does not have a way for me to do this. I thought to myself “How hard could this be?” and spent the rest of the night throwing this baby together:

Netflix Autoplay Bookmarklet
If you don’t know how bookmarklets work, simply drag the above link onto you link bar.

To use it, just start an episode of the show you want to watch, click the bookmark, enter the number of episodes you want it to play, then just let it run. It should keep playing episodes, until either the season ends, or it reaches the number you entered.


javascript:(function(){
	var eppsWatched = 1,
		movieIDrgx = /movieid=\d*[&|#]/,
		trackIDrgx = /trkid=\d*[&|#]/,
		episoIDrgx = /episodeMovieId=\d*[&|#]/,
		idregx = /\d+/,
		epps, movieId, trkId, eppId, sl, tmp;

	//grab netflix JS interface for player
	try {
		sl = netflix.Silverlight.MoviePlayer.getPlugin().getScriptInterface() || false;
	} catch (e) {
		sl = false;
	}		

	if(!sl) {
		alert('Please start the first episode you wish to watch.');
	} else {
		epps = prompt('How many episodes would you like to play?', '3'),
		//number of episodes the user wants to watch
		epps = parseInt(epps, 10);

		//extract the IDs from the url
		movieId = parseInt((idregx.exec(movieIDrgx.exec(window.location)) || [0])[0], 10);
		eppId 	= parseInt((idregx.exec(episoIDrgx.exec(window.location)) || [0])[0], 10);
		trkId 	= parseInt((idregx.exec(trackIDrgx.exec(window.location)) || [0])[0], 10);

		//add even to movie finished
		sl.OnMovieWatched = function() {
			var waiting = null; //make our timeer

			//check if we have finished watching
			if(eppsWatched < epps) {
				//move to the next episode
				if(trkId !== 0) {
					trkId++;
				}
				if(eppId !== 0) {
					eppId++;
				}
				if(movieId !== 0) {
					movieId++;
				}			

				if(!waiting) { //this event triggers about 2 minutes before the end of a show - and multiple times, my guess it is how they move their index to know they should start with the next epp next time
					waiting = setTimeout(function() {
						//tell netflix to play the next epp
						sl.PlayMovie({movieId: movieId, episodeMovieId: eppId, trackId: trkId});

						//increment our episode counter
						eppsWatched++;

						clearTimeout(waiting);
						waiting = null;
					},2*60*1000);
				}
			}
		}
	}
})();

Some caveats:

  • This (obviously) only works with shows, not movies, because there needs to be a "next episode".
  • This only works within a single season, so if you start with the last episode of a season, you will only ever see that single episode.
  • The movie ID seems to be the only one that matters, I haven't even seen the episodeMovieId used anywhere, I am incrementing all the IDs, it has worked for the 3 shows I have tried.
  • Netflix executes their "OnMovieWatched" event before the end of the movie, and I am not exactly sure how far before, so I just kind of guessed a bit.
  • This works for me in Chrome and Firefox, if you are using some other shitty browser, or an old version and it does not work for you, I don't care. Use a better browser.

    There is a good chance this does not work with all shows. In fact I would be shock if it worked on all shows. If you find a show it does not work on, feel free to let me know and I might take a look! If you want to fix it yourself and send me the update, even better!

    So, Netflix could easily stop this from working, but I hope they don't. Fortunately, probably only about 3 people read my blog, so I think this should be pretty safe!

    How did I figure this out? Well, I don't want to type that up right now, so it will have to wait for another post.

    I don’t GIT rebase.

    I switched my team over to GIT about a month ago. For the most part it has been ok, a few small problems here and there, a lot of learning. We ran into a situation that caused us quite a headache recently with changes that are not to go live being already in master. We had this same problem when using SVN, so it was not new, just crappy, as it always is. We had spent the last month however playing with GIT and we were all now very clear on how easy branching is in GIT. We decided at that point that we would adopt a more GITy approach to development and create new branches for all changes then only ‘merge’ them into master when they had been completed. This is when I started to do more research on rebasing…

    Note: I want to point out, this is about rebasing between branches from a remote repo that multiple people are could be working on. Rebasing in a single branch is/with only local work is…well, read about it somewhere else. It is easy.

    What is rebase?  What does it do?

    Rebase is like a merge, the difference is that it preserve the commit history. As Shakespeare once wrote, “Words, words, words!”. Here:

    So, that is really it, instead of having a single “merge commit” you get the entire commit history.

    Why would you want this? Well, our workflow is to work on a new feature (or bug fix) in a new branch, then when it is done move those changes into master and delete the branch. The problems is if you just use merge, you will end up with a master that is nothing but merge commits, so its revision history is pretty worthless. With rebase, we can get rid of the branch, but not lose its history. As far as we are concerned, once the feature is done, it is just “part of the projects past” and we don’t want to dig down into old broken branches if we need to go back to it.

    Sounds cool, but someone else already rebased their changes into master? / OH GOD EVERYTHING IS BLOWING UP

    Ah yes, this is where it gets a little tricky. So, say we have a team of multiple people, all working on their own branches, someone finishes their work, rebases it into master, now my branch is not coming off of origin/master…this does not seem good.

    Yeah, so I wish I had a picture for this, but I started making one and I got tired.

    You have 2 choices at this point.

    You can rebase master back into your branch

    This is what I tried first. This shoves all of the changes that were added to master to the bottom of your branch. This means your local branches commit history has changed and if you try to push, you will get an error. Basically, your local branch now looks like a totally different branch from what is in origin, this is bound to cause problems. You can do a “force push” which will basically says ‘screw what is in origin, use this’ which works, it is just crappy for anyone else working on that branch because everything goes a bit wonky.

    Note: People have been questioning me about what I said above. If your branch is only local (ie: has never been push, only committed to) you don’t have to force push because it does not exist in origin yet, so origin can’t complain. Also, if you are the only person who has done any work on the branch, doing a force isn’t really all that bad as you can only hurt yourself. I unfortunately do not have the luxury of working in an environment where either of these is usually the case.

    You can merge master into your branch

    This seems to be the way to go. Doing this means your commit history stays the same and you just get the changes that were added to master after you branched as a ‘merge commit’. Now, when your branch is done and you rebase back into master, you are already up to date with what is there, and it can easily add the commits from your branch on to the top of master.

    This won’t look as pretty as always rebasing. If you rebase it makes it appear that your branch is always coming off of the latest version of master. If you merge, your branch will come off of master, then come back together where you merge, and basically flow in and out as you merge/make changes. But then again, we are planning on deleting the branch when it is done anyway, so who cares if it does not look super pretty…Also, I guess as long as the workflow is good, I should not be caring about the prettyness of my repo, stupid brain!

    Recap

    Workflow: when you start working, you create a branch off of master and make your changes. If changes are made to master (say someone finishes the changes in their branch and rebases it into master) while you are working, merge them back into your branch. When your branch is done, rebase it into master and delete the branch.

    Basically, you only ever rebase master. Other branches should have changes from master merged into them.

    Is this the commonly accepted workflow? Uh..sure…why not? It is here. I would change it if someone proposed something better. Like I said, I have spent a lot of time reading about these things, there is a lot of information…I just don’t see a lot of information that answers the question I had when trying to figure all of this out.

    I wrote this up because even though there is tons of information about all these things online, nobody seems to provide it in a manner that was clear to me until I spent a good deal of time playing with it myself…I have probably not really helped that either.

    PS: Yes, I am very well aware of how lame and I am sure over-done the joke (if you can even call it that) in my title is. I am lame.

    Cascading form require/validation

    I ran into an interesting problem recently dealing with some complicated form validation. The issue was that we had several forms with fields that were required or optional based on if other inputs were filled out, with the requirements falling in a specific order. I came up with an interesting solution that looks at a data-ignore-validation attribute on the current form element and, if it exists will check the form element(s) with that name to see if they are filled out, or if they have a similar data-ignore-validation attribute meaning they can possibly be ignored as well. We have a nice little recursive solution that will check all of the form elements in a chain to see if the current one can be ignored.

    All we need to do is send the following function the form element to be checked as well as our form, and it will tell us if the element passed in should be ignored.

    [sourcecode language=’javascript’]
    function ignoreField(elem, form) {
    //if we have an data-ignore-validation property, we can ignore _this_ .val() if the field with the name = ignore-validation has a .val() set
    var ignoreOn = $(elem).data(‘ignoreValidation’);
    if(ignoreOn) {
    ignoreOn = ignoreOn.split(‘,’);
    //We use this var to hold the complete list of for elements we need to check
    var $ignoreFields = $();

    //get all of the fields this one can be ignored on
    for(var i = 0; i < ignoreOn.length; i++) { $ignoreFields = $ignoreFields.add(form.find('input[name="'+ignoreOn[i]+'"], select[name="'+ignoreOn[i]+'"]')); } //(recursively) check if any of the fields in the ignoreValidation have a value set for(var i = 0; i < $ignoreFields.length; i++) { if($ignoreFields.val()) { return true; } return ignoreField($ignoreFields.eq(i), form); } } return false; } [/sourcecode] Of course we do have an issue here where an inexperienced developer could put in a circular reference and we would end up with and infinite loop, but I leave it to you to either fix that issue, or just be smart enough not to do that.

    I’m pretty sure I am not the only geek in this town.

    Font graffiti found in the men's bathroom @ The Vault bar, Asheville, NC

    So, I am not really a font geek (other than the usual hatred for Comic Sans), but apparently there are a few in my town. I found this on the wall of the men’s bathroom at The Vault in download Asheville, NC. I think it is fair to assume that there are a few other geeks in your town when this is the kind of graffiti found on the walls of a bar.

    Duplicate IDs are baaaaaaaaaaaaaaad

    If you are not already very aware, duplicate IDs in your markup are a very bad thing. Now, technically they will work fine in CSS, and can possibly even make it faster, BUT they WILL break your javascript. Basically when you have multiple elements with the same ID what the browser will do is unpredictable.

    Sometimes in some browsers you might get what you want, other times in other browsers the wrong thing will happen, sometimes nothing will happen. You never know. On many different occasions I have been helping someone with some javascript, or fixing a website for a job and things just won’t work, and eventually I find the problem to be duplicate IDs. Dealing with this problem again today, I wrote a little jQuery plugin that checks your page for duplicate IDs. It is pretty simple, and you should never actually have this on a production site, it is just something you run while you are developing so you can yell at the jackass who wrote crappy markup to make sure you aren’t doing things you shouldn’t. The full code for the plugin is this:

    
    (function($) {
    $.dupCheck = function() {
    var ids = [], dups = [], dupCount = 0;
    
    //this just displays the result if there is no console
    if (!window.console) {
    var console = {
    log: function(msg) {
    $('body').append(msg);
    }
    };
    } else {
    console = window.console;
    }
    
    $('*').each(function() {
    var id = this.id, i = 0, len = ids.length, isDup = false;
    
    if(id) {
    for(i=0 ; i < len ; i++) { //loop over the IDs we have already processed to see if the current one is a dup if(!isDup && id === ids[i]) { //if the ID is a dup, add it to the dups list and set our bool if(checkIfAlreadyDup(id) === false) { dups.push(id); } isDup = true; dupCount++; break; } } if(!isDup) { //if the ID was not a dup, add it to the list of processed IDs ids.push(id); } else { //if the ID was not a dup, clear out our bool isDup = false; } } }); if(dups.length === 0) { console.log('No duplicate IDs found =)'); } else { console.log('' + dups.length + ' duplicate IDs found repeated ' + dupCount + ' times =('); } function checkIfAlreadyDup(id) { var len = dups.length, i = 0; for(i = 0; i < len; i++) { if(dups[i] === id) { return true; } } return false; } return dups; } })(jQuery); 

    The way to use this is to load the plugin (or even just paste the code into your javascript console) then run the function:

     var dups = $.dupCheck(); 

    You obviously need to do this after the document is ready, and after any offending javascript/ajax calls have completed. It will output a message telling you how may duplicate IDs you have, and it returns an array of the IDs that are duplicates. It is pretty simple, but if you are banging you head against the wall trying to figure out why your javascript is not working, just run it, and it might give you the answer. Download: Dupcheck.js

    And I have now run this on this page, apparently I have a duplicate ID! Maybe I need to just get rid of that facebook ‘like’ button.

    Updates to my pub/sub

    I have updated my pub/sub plugin.  The two changes are:

    It now uses the full words, publish, subscribe, and unsubscribe – this change was because with 1.5, jQuery now has a sub method which I do not want to overwrite.

    Also, you can now publish and subscribe multiple tags at once using a space separated list like so:

    
    
    $.publish('first/tag second/tag');
    
    $.subscribe('first/tag second/tag', function(){/*do something*/});
    
    

    This just make it more like the rest of jQuery events and such.

    The new version can be downloaded here:

    Live DOM sub/pub

    Got to hang out at Google for dayofjs

    I got to go to the dayofjs conference (thanks MJG!) Not only was the conference really interesting, but I also got to hang out at Google which was cool. So, I snapped a few pics, as one does.

    This is where I found out that I parked about as far as I could have from where I wanted to be.

    Google lunch.

    Can’t really complain about the day

    Ahh, the free beverage fridge

    This doesn’t have anything to do with Google or Dayofjs, but I got In-and-Out on my way home after being stuck in traffic for 2 hours, because it has been a long time since I have had it.


    The mobile talks of the day were about:

    Sencha Touch: http://www.sencha.com/products/touch/

    Appcelerator’s Titanium: http://www.appcelerator.com/

    jQuery Mobile: http://jquerymobile.com/

    There was also a fair amount of talk about PhoneGap: http://www.phonegap.com/

    Yehuda Katz also gave a talk about Sproutcore that was really good.  It was not really a mobile talk, but it was still very informative and interesting.

    Unit testing jQuery w/ QUnit

    So, like most developers I love having unit test, but I hate writing them. Really figuring out the best way to write them and have them work well, is a pain in the ass, and that is what I hate. I have spent some time recently playing with QUnit and despite the number fact that I have found a number of posts online about it, none of them have provided (in my mind) a good way to test a site. The ideas used work well for testing a library, or specific functions, but I don’t write libraries or one off functions, I develop websites. So, I have come up with a method that I like that allows me to test my sites in a way that I like.

    The Problems:

    The unit testing information I have found mainly deals with testing libraries – this is no good for me.
    Testing involves putting QUnit on your page, along with its specific markup – although you can have a “debug” mode for this, it just seems like a bad idea, for a number of reasons, including it not really being an accurate representation of your page, and it does not work across page requests.
    Using headless testing – it is cool, but not cross browser or even really O/S ready.
    Using something like Selenium – also cool, but involves building tests using a new tool that uses java.

    My solution:

    I have come up with my own solution, which, I admit is not perfect, but it does a darn good job of testing what I need tested. Basically it involves having a very simply html page next to your site, that loads your site in an iframe and runs the tests on it. It is cross-browser and cross-O/S safe, it works for pretty much any site, and uses the same javascript/jQuery I already write every day.

    There are a couple of problems still with my solution, but they are pretty easy to overcome, and writing tests becomes very simple. The basic page we need to get started with the testing is this:

    
    
     
    
    
    
     
    
    

    QUnit Tests

      test markup, will be hidden
       

      Note: We can (and probably should) actually move our tests into different .js files making it much easier to deal with as our number of tests grow.

      We are doing a couple of interesting things here, first we are testing multiple pages at one time. Every time a new page loads up, we grab the jQuery refrence from inside of it, then run the tests that are specified for that page.

      Another cool thing is how we get the jQuery reference for each page.

      We start by loading jQuery using noConflict(true)

      This is because we need to grab the jQuery instance from inside the actual website. When jQuery attaches events to DOM elements, it actually saves those events inside of itself, so although we can trigger events inside of the page itself using our external jQuery, triggering events inside of the iframe will not actually fire any events that were created inside the page. So, instead we grab the jQuery that is actually on the page and trigger the events using it. This has the added bonus that because it is the reference from inside the page we are loading, we can reference DOM nodes as though we were working that that page directly.

      Here we grab the jQuery reference from inside the iframe with:

      
      $ = window.frames[0].jQuery;
      

      Note: For ANY of this to work we must adhere to SOP, this is why we load this up on a server, side by side with our site. This is why I am loading up the iframe using location.protocol + ‘//’ + location.hostname rather than typing out the URL. This will allow it to work as we move between servers and such.

      One of the cool things we are doing here is tying the starting of the tests to a button, so we can get the page to any state necessary before starting the tests – such as logging in, waiting for ajax requests to finish, or navigating to somewhere specific (not that we couldn’t do these in the tests, it just gives us options.)
      Also, before we run our tests we are running the 2 following commands:

      
      $.ajax({async: false});
      $.fx.off = true;
      

      This will make our ajax requests and animations synchronous. Although you would never do this (especially the ajax one) on a site, for our tests it is necessary to ensure that the page is ready before we perform each test.

      Now that we have done this we can run our test just like we were on the page directly.

      We can select elements and trigger events on them, like so:

      
      $(elem).click(); or $(elem).trigger(‘click’);
      $(elem).mousedown(); or $(elem).trigger(‘mousedown);
      Test things, such as visibility:
      $(elem).is(‘:visible’);
      

      Basically anything you would normally do. Simple and easy.

      This is not a perfect way to test your events, it is however pretty good, and will let us know when we break things.

      Here are some examples of tests in action. I hope this helps you get started with unit testing.

      Tests in action!

      Playing with CSS hooks

      So, I wanted to give the new CSS hooks in jQuery a try. Unfortunately, the usually well documented jQuery has this as the total documentation on the hooks:

      As of version 1.4.3, jQuery’s CSS module was rewritten to provide a set of “hooks” for the .css() method. This advanced feature, $.cssHooks, allows for fine-grained control over the way jQuery handles certain style properties and enables the creation of custom, browser-normalized properties. The $.cssHooks object also extends the set of properties available to the .animate() method.

      Which isn’t much to go on. So, I broke out the old source code to see whats up. Basically, you can hook into a CSS property and create setters and getters for it. I started by making a simple hook that allows you to use show and hide on the display property, and have it call show() and hide(). The code is pretty simple:

      
      jQuery.cssHooks.display = {
      set: function(elem, value) {
      if (typeof (value) === 'string') {
      if (value === 'hide') {
      $(elem).hide();
      } else if (value === 'show') {
      $(elem).show();
      } else {
      elem.style['display'] = value;
      }
      } else {
      if (value.hide) {
      $(elem).hide(value.hide);
      } else if (value.show) {
      $(elem).show(value.show);
      } else {
      elem.style['display'] = value;
      }
      }
      }
      };
      

      This simply checks if you are passing in a string or an object, and then calls show/hide with the supplied value (in the case of an object.) If you are not using show/hide it runs node.style[‘display’] = value; which is what normally happens when you call .css.

      I made an example for it here:

      I’m a thing!




      The code here is pretty simple too:

      
      $('#hookButton').click(function(e) {
      var $this = $(this);
      if($this.text() === 'Hide') {
      $('#cssHookTest').css('display', 'hide');
      $this.text('Show');
      } else {
      $('#cssHookTest').css('display', 'show');
      $this.text('Hide');
      }
      });
      
      $('#hookButton2').click(function(e) {
      var $this = $(this);
      if($this.text() === 'Hide Slow') {
      $('#cssHookTest').css('display', {'hide': 'slow'});
      $this.text('Show Slow');
      } else {
      $('#cssHookTest').css('display', {'show': 'slow'});
      $this.text('Hide Slow');
      }
      });
      
      $('#hookButton3').click(function(e) {
      var $this = $(this);
      if($this.text() === 'Hide REALLY Slow') {
      $('#cssHookTest').css('display', {'hide': 1400});
      $this.text('Show REALLY Slow');
      } else {
      $('#cssHookTest').css('display', {'show': 1400});
      $this.text('Hide REALLY Slow');
      }
      });
      
      $('#hookButton4').click(function(e) {
      var $this = $(this);
      if($this.text() === 'Hide Normal') {
      $('#cssHookTest').css('display', 'none');
      $this.text('Show Slow');
      } else {
      $('#cssHookTest').css('display', 'block');
      $this.text('Hide Normal');
      }
      });
      
      

      So, that’s about it, the CSS hooks, although called an advanced feature are really not that hard to use. So, give ’em a try.

      jQuery Live DOM sub/pub plugin

      Updated 2/10/10 – for the updates to the plugin (See: here)

      I made a new jQuery plugin the other day, it is pretty simple, it expands on the idea subscribe/publish idea. Basically it allows you to bind subscriptions to DOM nodes.

      You use it like this:

      
      
      //subscribe a node
      $(selector).subscribe(
      'subscription/name',
      function(subName, [arg1], [arg2],...)
      {/*do something*/}
      );
      
      //publish
      $.publish(
      'subscription/name', [arg1], [arg2],...
      );
      

      The idea is that you are probably using sub/pub to keep your DOM interaction separate from your non DOM related javascript. So, when you publish, there is a good chance that the subscriber will take the published information and do something to the DOM based on it. Inside of you sub callback, this is the DOM node following jQuerys normal pattern for this.

      You can still setup normal subscriptions like so:

      
      $.subscribe(
      'subscription/name',
      function(subName, [arg1], [arg2],...)
      {/*do something*/}
      );
      

      One thing to note is that nothing is actually bound to the nodes. In reality the selector that you give is simply saved, and used again when you publish, so this works like .live in that it will even work on nodes added to the DOM after you subscribe. Also like .live though, you must provide the full selector as a string, and you can’t use any of the traversal methods to select your nodes.

      Demo:

      You must enter something here
      Watch me.

      The code for this is :

      
      $('#formId').submit(function(e){
      validateForm();
      return false;
      });
      
      $('#response').subscribe('formValidateDone', function(subName, result) {
      if(result) {
      $(this).html('The form is valid!');
      } else {
      $(this).html('Please try again!');
      }
      });
      
      function validateForm() {
      $.publish('formValidateDone', $('#textInput').val() !== '');
      }

      Plugin Link: Plugin page

      Download: Download