For Your Eyes Only

***For Your Eyes Only*** is a 1981 British spy film directed by John Glen (in his feature directorial debut) and produced by Albert R. Broccoli. The film stars Roger Moore as the fictional MI6 agent James Bond, and co-stars Carole Bouquet, Chaim Topol, Lynn-Holly Johnson, and Julian Glover.

The twelfth film in the *James Bond* franchise produced by Eon Productions, *For Your Eyes Only* was written by Richard Maibaum and Michael G. Wilson, based on two Ian Fleming short stories "For Your Eyes Only" and "Risico". In the plot, Bond attempts to locate a missile command system while becoming tangled in a web of deception spun by rival Greek businessmen along with Melina Havelock, a woman seeking to avenge the murder of her parents. Some writing elements were inspired by the novels *Live and Let Die*, *Goldfinger*, and *On Her Majesty's Secret Service*.

After the science fiction-focused *Moonraker*, the producers wanted a return to the style of the early Bond films and the works of 007 creator Fleming. *For Your Eyes Only* followed a grittier, more realistic approach and a narrative theme of revenge and its consequences rather than the fantasy narrative of *Moonraker*. Filming locations included Greece, Italy and England, while underwater footage was shot in The Bahamas. Scottish pop star Sheena Easton performed the title theme song.

*For Your Eyes Only* was released in the UK on 24 June 1981 and in the US two days later; it received a mixed to positive critical reception. The film's reputation has improved over time, with reviewers praising the more serious tone. The film was a financial success, generating $195.3 million worldwide. This was the final Bond film to be distributed solely by United Artists; the company was absorbed by Metro-Goldwyn-Mayer soon after this film's release.

## Plot

The British information gathering vessel *St Georges*, which holds the Automatic Targeting Attack Communicator (ATAC), the system used by the Ministry of Defence to co-ordinate the Royal Navy's fleet of Polaris submarines, is sunk after accidentally trawling an old naval mine in the Ionian Sea. A marine archaeologist, Sir Timothy Havelock, is asked by the British to secretly locate the *St Georges*. However, he and his wife are murdered by a Cuban hitman, Hector Gonzales. His daughter, Melina Havelock, witnesses the murder and vows revenge.

The head of the KGB, General Gogol, has also learned of the fate of the *St Georges* and already notified his contact in Greece. MI6 agent James Bond is ordered by the Minister of Defence, Sir Frederick Gray, and MI6 Chief of Staff Bill Tanner to retrieve the ATAC before the Soviets since the transmitter could order attacks by the submarines' Polaris ballistic missiles. Bond goes to Spain to find out who hired Gonzales.

While spying on Gonzales's villa, Bond is captured by his men, but escapes as Gonzales is killed by a crossbow bolt. Outside, he finds the assassin was Melina and the two escape. With the help of Bond, Q uses computerised technology to identify the man Bond saw paying off Gonzales as Emile Leopold Locque, and then goes to Locque's possible base in Cortina, Italy. There Bond meets his contact, Luigi Ferrara, and a well-connected Greek businessman and intelligence informant, Aris Kristatos, who tells Bond that Locque is employed by Milos Columbo, known as "the Dove" in the Greek underworld, Kristatos's former resistance partner during the Second World War. After Bond goes with Kristatos's protégée, figure skater Bibi Dahl, to a biathlon course, a group of three men, which includes East German biathlete Eric Kriegler, chases Bond, trying to kill him. Bond escapes and then goes with Ferrara to bid Bibi farewell in an ice rink, where he fends off another attempt on his life by men in ice hockey gear. Ferrara is killed in Bond's car, with a dove pin in his hand. Bond then travels to Corfu in pursuit of Columbo.

There, at the casino, Bond meets Kristatos and asks how to meet Columbo, not knowing that Columbo's men are secretly recording their conversation. After Columbo and his mistress, Countess Lisl von Schlaf, argue, Bond offers to escort her home with Kristatos's car and driver. The two then spend the night together. In the morning, Lisl and Bond are ambushed by Locque and Lisl is killed. Bond is captured by Columbo's men before Locque can kill him; Columbo then tells Bond that Locque was actually hired by Kristatos, who is working for the KGB to retrieve the ATAC. Bond accompanies Columbo and his crew on a raid on one of Kristatos's opium-processing warehouses in Albania, where Bond uncovers naval mines similar to the one that sank the *St Georges*, suggesting it was not an accident. After the base is destroyed, Bond chases Locque and kills him.

Afterwards, Bond meets Melina, and they recover the ATAC from the wreckage of the *St Georges*, but Kristatos is waiting for them when they surface and he takes the ATAC. After the two escape an assassination attempt, they discover Kristatos's rendezvous point when Melina's parrot repeats the phrase "ATAC to St Cyril's". With the help of Columbo and his men, Bond and Melina break into St Cyril's, an abandoned mountaintop monastery. As Columbo confronts Kristatos, Bond kills Kriegler.

Bond retrieves the ATAC system and stops Melina from killing Kristatos after he surrenders. Kristatos tries to kill Bond with a hidden flick knife, but is killed by a knife thrown by Columbo; Gogol arrives by helicopter to collect the ATAC, but Bond throws it off the cliff. Bond and Melina later spend a romantic evening aboard her father's yacht when he receives a call from the Prime Minister.

## Cast

* Roger Moore as James Bond, MI6 agent 007, who is sent to retrieve a stolen "ATAC" system that could be misused for controlling British submarines.
* Carole Bouquet as Melina Havelock, the daughter of marine archaeologists who are murdered while tracking down the ATAC's whereabouts. Bouquet had auditioned for the role of Holly Goodhead in *Moonraker*, but was unsuccessful.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**
* Topol as Milos Columbo, Kristatos's enemy and former smuggling partner. He convinces Bond to side with him. Named after Gioacchino Colombo, the Ferrari engine designer, specifically Ferrari 125, which Fleming admired.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Topol suggested the pistachios as a trademark of the character, which are used in the warehouse assault scene to orient Columbo's men on where to shoot.
* Lynn-Holly Johnson as Bibi Dahl, an ice-skating prodigy who has a crush on Bond; she is training with the financial support of Kristatos. Johnson was an ice skater before turning to acting, and achieved second place at the novice level of the 1974 United States Figure Skating Championships. Michael Wilson explained that she was written as "a character that antagonised Bond."
* Julian Glover as Aristotle Kristatos, a former war hero turned smuggler. Initially shown as an ally, later as the main villain, planning to expand his fortune by selling the ATAC to the KGB. Glover had been shortlisted as a possible Bond for *Live and Let Die*, eventually losing out to Moore.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**
* Cassandra Harris as Countess Lisl von Schlaf, Columbo's mistress. At the time of filming Harris was married to future Bond actor Pierce Brosnan, and the couple lunched with the film's producer Albert R. Broccoli during filming.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**
* Michael Gothard as Emile Leopold Locque, a Belgian hired assassin known by his octagonal-rimmed glasses. He turns out to be an associate of Kristatos.
* Jill Bennett as Jacoba Brink, Bibi's skating coach.
* Jack Hedley as Sir Timothy Havelock, Melina's father and a marine archaeologist hired by the British Secret Service to secretly locate the wreck of *St. Georges*.
* Walter Gotell as General Anatoly Gogol, head of the KGB.
* James Villiers as Bill Tanner, MI6 Chief of Staff. The role of Tanner first appeared on film in *The Man with the Golden Gun*, although in an un-credited capacity. Villiers presumed he would play the role of M in subsequent films and was disappointed not to be asked; the producers thought him too young for the role and wanted an actor in his 70s.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**
* Desmond Llewelyn as Q, the head of MI6's technical department.
* John Moreno as Luigi Ferrara, 007's MI6 contact in northern Italy.
* Geoffrey Keen as Sir Frederick Gray (credited as Minister of Defence), a politician in the British government. The role, along with Bill Tanner as Chief of Staff, was used to brief Bond in place of M, following the death of Bernard Lee.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**
* Lois Maxwell as Miss Moneypenny, M's secretary.
* John Wyman as Eric Kriegler, an East German Olympic class biathete. He is revealed to be Kristatos's second in-command and a KGB contact.

John Hollis plays the bald villain in wheelchair,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** voiced by Robert Rietti.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** The character appears in the pre-credits sequence and is both unnamed and uncredited. The character contains a number of characteristics of Ernst Stavro Blofeld,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** but could not be identified as such because of the legal reasons surrounding the *Thunderball* controversy with Kevin McClory claiming sole rights to the Blofeld character, a claim disputed by Eon.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Bob Simmons, who previously portrayed Bond in the gun barrel sequences in the first three films and SPECTRE agent Colonel Jacques Bouvar in *Thunderball*, cameos as another villain as Gonzales's henchman who falls victim to Bond's exploding Lotus.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Victor Tourjansky, the assistant director, has his third cameo in the *Bond* films as a drinking tourist; he is credited as part of the Ski Team for Stunts.
Janet Brown plays the British Prime Minister Margaret Thatcher, who appears in the closing scene alongside John Wells as Denis Thatcher.

## Production

“*We had gone as far as we could into space. We needed a change of some sort, back to the grass roots of Bond. We wanted to make the new film more of a thriller than a romp, without losing sight of what made Bond famous – its humour.*”
―John Glen

Ian Fleming wrote the original story "For Your Eyes Only" as an episode of a cancelled *James Bond* television series at CBS in 1958. Eon Productions originally intended to produce *For Your Eyes Only* after *The Spy Who Loved Me*. However, after the success of *Star Wars* in 1977 the producers decided to produce *Moonraker* instead. *Moonraker* was successful yet was very expensive to produce, and shortly afterwards United Artists suffered a major financial flop with Michael Cimino's *Heaven's Gate*. This, along with the early 1980s recession, required *For Your Eyes Only* to have a lower budget. *For Your Eyes Only* marked a change in the make up of the production crew. The previous series directors Terence Young, Guy Hamilton, Lewis Gilbert, and Peter Hunt were unable to direct because the studio could not afford to hire them, and John Glen was promoted from his duties as a film editor to director, a position he would occupy for four subsequent films. Glen brought on much of his second-unit direction team from *The Spy Who Loved Me* and *Moonraker*, including cinematographer Alan Hume. The transition in directors and lower budget resulted in a harder-edged directorial style, with less emphasis on gadgetry and large action sequences in huge arenas as was favored by Gilbert in the previous two films.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Emphasis was placed on tension, plot and character in addition to a return to Bond's more serious roots,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** whilst *For Your Eyes Only* "showed a clear attempt to activate some lapsed and inactive parts of the Bond mythology."**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

The film was also a deliberate effort to bring the series more back to reality, following the success of *Moonraker* in 1979.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** As co-writer Michael G. Wilson pointed out, "If we went through the path of *Moonraker* things would just get more outlandish, so we needed to get back to basics". To that end, the story that emerged was simpler, not one in which the world was at risk, but returning the series to that of a Cold War thriller; Bond would also rely more on his wits than gadgets to survive. Glen decided to symbolically represent it with a scene where Bond's Lotus blows itself up and forces 007 to rely on Melina's more humble Citroën 2CV. Since Ken Adam was busy with *Pennies from Heaven*, Peter Lamont, who had worked in the art department since *Goldfinger*, was promoted to production designer. Following a suggestion of Glen, Lamont created realistic scenery, instead of the elaborate set pieces for which the series had been known.

### Writing

Before the project was postponed in favor of *Moonraker*, Tom Mankiewicz had written a storyline and Christopher Wood submitted a first draft in January 1978. However, their screenplay did not influence the final film. Richard Maibaum was once again the scriptwriter for the story, assisted by Michael G. Wilson.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** According to Wilson, the ideas from stories could have come from anyone as the outlines were worked out in committee that could include Broccoli, Maibaum, Wilson and stunt coordinators.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Much of the inspiration for the stories for the film came from two Ian Fleming short stories from the collection *For Your Eyes Only*: *Risico* and *For Your Eyes Only*.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Another set-piece from the novel of *Live and Let Die* – the keelhauling – which was unused in the film of the same name, was also inserted into the plot.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Other ideas from Fleming were also used in *For Your Eyes Only*, such as the Identigraph which comes from the novel *Goldfinger*, where it was originally called the "Identicast".**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** These elements from Fleming's stories were mixed with a Cold War story centred on the MacGuffin of the ATAC.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** An initial treatment for this film was submitted by Ronald Hardy, an English novelist and screenwriter in 1979. Hardy's treatment included the involvement of a character named Julia Havelock whose parents were assassinated by a man named Gonzales.

The pre-title sequence of *For Your Eyes Only* has been described as either "out-of place and disappointing"**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** or "roaringly enjoyable".**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** The scene was shot to introduce a potential new Bond to audiences, thus linking the new actor to elements from previous Bond films**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** (see casting, below).

The sequence begins with Bond laying flowers at the grave of his wife Tracy Bond, before a Universal Exports helicopter picks him up for an emergency. Control of the helicopter is taken over by remote control by a bald man in a grey Nehru jacket with a white cat. This character is unnamed in either the film or the credits, although he looks and sounds like Ernst Stavro Blofeld as played by Donald Pleasence or Telly Savalas.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Director John Glen referred to the identity of the villain obliquely: "We just let people use their imaginations and draw their own conclusions ... It's a legal thing".**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Originally the character was going to be explicitly identified as Blofeld, but was deliberately not named due to copyright restrictions with Kevin McClory, who owned the film rights to *Thunderball*, which supposedly includes the character Ernst Stavro Blofeld, the organisation SPECTRE, and other material associated with the development of *Thunderball*. Eon disputed McClory's ownership of the Blofeld character, but decided not to use him again: the scene was "a deliberate statement by Broccoli of his lack of need to use the character."**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

Maibaum later said "We tried to return to the earlier films with *For Your Eyes Only* but we didn't have Sean to make it real. And I was very disappointed with the way the love story was handled. The whole idea was that the great lover James Bond can't get to first base with this woman because she was so obsessed with avenging her parents' death. Nothing was ever done with it. It was as if the director didn't feel there was a love story there at all."

### Casting

Roger Moore had originally signed a three-film contract with Eon Productions, which covered his first three appearances up to *The Spy Who Loved Me*. Subsequent to this, the actor negotiated contracts on a film-by-film basis.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Uncertainty surrounding his involvement in *For Your Eyes Only*, considering his age, led to other actors being considered to take over, including Lewis Collins, known in the UK for his portrayal of Bodie in *The Professionals*; Ian Ogilvy, known for role as Simon Templar in *Return of the Saint* (played by Moore in the original series); Michael Billington, who previously appeared in *The Spy Who Loved Me* as Agent XXX's ill-fated lover Sergei Barzov, (Billington's screen test for *For Your Eyes Only* was one of the five occasions he auditioned for the role of Bond) and Michael Jayston, who had appeared as the eponymous spy in the British TV series of *Quiller***Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** (Jayston eventually played Bond in a BBC Radio production of *You Only Live Twice* in 1985).**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Timothy Dalton was strongly considered but Dalton declined, as he disliked the direction the series was taking at the time. When Moore discovered that Broccoli and Saltzman were screen-testing actors without his knowledge, he announced in the *Daily Mail* that he would not return to play Bond. However, the producers convinced him to return just two weeks later.

Bernard Lee died of cancer on 16 January 1981, after filming began on *For Your Eyes Only*, but before he could film his scenes as M, the head of MI6, as he had done in the previous eleven films of the series. Out of respect, no new actor was hired to assume the role as Broccoli refused to have the character recast and, instead, the script was re-written so that the character is said to be on leave, letting Chief of Staff Bill Tanner take over the role as acting head of MI6 and briefing Bond alongside the Minister of Defence.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** This is only the second time M has been omitted from a Bond production, the first being the 1954 TV adaptation of *Casino Royale*. Ironically, the original "For Your Eyes Only" short story dealt with Bond being given a personal mission by M, one of the few times in the Fleming canon that 007 did his superior a direct favor.

Chaim Topol was cast following a suggestion by Broccoli's wife Dana, while Julian Glover joined the cast as the producers felt he was stylish – Glover was even considered to play Bond at some point, but Michael G. Wilson stated that "when we first thought of him he was too young, and by the time of *For Your Eyes Only* he was too old". Carole Bouquet was a suggestion of United Artists publicist Jerry Juroe, and after Glen and Broccoli saw her in *That Obscure Object of Desire*, they went to Rome to invite Bouquet for the role of Melina.

### Filming

 File:2cv 007.jpg
Production of *For Your Eyes Only* began on 2 September 1980 in the North Sea, with three days shooting exterior scenes with the *St Georges*. Although the previous film had been shot almost entirely outside of the United Kingdom, the new Conservative Prime Minister Margaret Thatcher's tax cuts allowed the shoot to return to Britain. The interiors were shot later in Pinewood Studios, as well as the ship's explosion, which was done with a miniature in Pinewood's tank on the 007 Stage. On 15 September principal photography started on Corfu at the Villa Sylva at Kanoni, above Corfu Town,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** which acted as the location of the Spanish villa. Many of the local houses were painted white for scenographic reasons. Glen opted to use the local slopes and olive trees for the chase scene between Melina's Citroën 2CV and Gonzales's men driving Peugeot 504s. The scene was shot across twelve days, with stunt driver Rémy Julienne – who would remain in the series up until *GoldenEye* – driving the Citroën. Four 2CVs were used, with modifications for the stunts – all had more powerful flat-four engines, and one received a special revolving plate on its roof so it could get turned upside down.

In October filming moved to other Greek locations, including Meteora and the Achilleion. In November, the main unit moved to England, which included interior work in Pinewood, while the second unit shot underwater scenes in The Bahamas. On 1 January 1981, production moved to Cortina d'Ampezzo in Italy, where filming wrapped in February. Since it was not snowing in Cortina d'Ampezzo by the time of filming, the producers had to pay for trucks to bring snow from nearby mountains, which was then dumped in the city's streets.

Many of the underwater scenes, especially involving close-ups of Bond and Melina, were faked on a dry soundstage. A combination of lighting effects, slow-motion photography, wind, and bubbles added in post-production, gave the illusion of the actors being underwater.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Actress Carole Bouquet reportedly had a pre-existing health condition that prevented her from performing underwater stunt work. Aquatic scenes were done by a team led by Al Giddings, who had previously worked on *The Deep*, and filmed in either Pinewood's tank on the 007 Stage or an underwater set built in the Bahamas. Production designer Peter Lamont and his team developed two working props for the submarine Neptune, as well as a mock-up with a fake bottom.

Roger Moore was reluctant to film the scene of Bond kicking a car, with Locque inside, over the edge of a cliff, saying that it "was Bond-like, but not Roger Moore Bond-like." Michael G. Wilson later said that Moore had to be persuaded to be more ruthless than he felt comfortable.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Wilson also added that he and Richard Maibaum, along with John Glen, toyed with other ideas surrounding that scene, but ultimately everyone, even Moore, agreed to do the scene as originally written.

 File:Meteora Agios Triadas IMG 7632.jpg
For the Meteora shoots, a Greek bishop was paid to allow filming in the monasteries, but the uninformed Eastern Orthodox monks were mostly critical of production rolling in their installations. After a trial in the Greek Supreme Court, it was decided that the monks' only property were the interiors – the exteriors and surrounding landscapes were from the local government. In protest, the monks remained shut inside the monasteries during the shooting, and tried to sabotage production as much as possible, hanging their washing out of their windows**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** and covering the principal monastery with plastic bunting and flags to spoil the shots, and placing oil drums to prevent the film crew from landing helicopters. The production team solved the problem with back lighting, matte paintings, and building both a similar scenographic monastery on a nearby unoccupied rock, and a monastery set in Pinewood.

Roger Moore said he had a great fear of heights, and to do the climbing in Greece, he resorted to moderate drinking to calm his nerves. Later in that same sequence, Rick Sylvester, a stuntman who had previously performed the pre-credits ski jump in *The Spy Who Loved Me*, undertook the stunt of Bond falling off the side of the cliff. The stunt was dangerous, since the sudden rope jerk at the bottom could be fatal. Special effects supervisor Derek Meddings developed a system that would dampen the stop, but Sylvester recalled that his nerves nearly got the better of him: "From where we were [shooting], you could see the local cemetery; and the box [to stop my fall] looked like a casket. You didn't need to be an English major to connect the dots." The stunt went off without a problem.

Bond cameraman and professional skier Willy Bogner, Jr. was promoted to director of a second unit involving ski footage. Bogner designed the ski chase on the bobsleigh track of Cortina d'Ampezzo hoping to surpass his work in both *On Her Majesty's Secret Service* and *The Spy Who Loved Me*. To allow better filming, Bogner developed both a system where he was attached to a bobsleigh, allowing to film the vehicle or behind it, and a set of skis that allowed him to ski forwards and backwards to get the best shots.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** In February 1981, on the final day of filming the bobsleigh chase, one of the stuntmen driving a sleigh, 23-year-old Paolo Rigon, was killed when he became trapped under the bob. The incident, which took place a week after the FIBT World Championships 1981 where USA-1 bobsled driver James Morgan was killed in a crash during the four-man sled competition, resulted in the shortening of the track for future FIBT events.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

The pre-credits sequence used a church in Stoke Poges, Buckinghamshire as a cemetery, while the helicopter scenes were filmed at the abandoned Beckton Gas Works in London. The gas works were also the location for some of Stanley Kubrick's film, *Full Metal Jacket* (1987).**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Director John Glen gained the idea for the remote-controlled helicopter after seeing a child playing with an RC car. Since flying a helicopter through a warehouse was thought to be too dangerous, the scene was shot using forced perspective. A smaller mock-up was built by Derek Meddings' team closer to the camera that the stunt pilot Marc Wolff flew behind and this made it seem as if the helicopter was entering the warehouse. The footage inside the building was shot on location, though with a life-sized helicopter model which stood over a rail. Stuntman Martin Grace was a stand-in as Bond when the agent is dangling outside the flying helicopter, while Roger Moore himself was used in the scenes inside the model.

### Music

*Main article: For Your Eyes Only (soundtrack)*
The score of *For Your Eyes Only* was written by Bill Conti, who retained some John Barry-influenced brass elements in the score, but also added elements of dance and funk music. While one reviewer observed that "Bill Conti's score is a constant source of annoyance", another claimed that "In the end, *For Your Eyes Only* stands as one of the best James Bond film scores of the 1980s."

The title song, written by Conti and Michael Leeson, was sung by Sheena Easton, who was the first title song artist to appear on screen in a Bond film,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** as designer Maurice Binder liked Easton's appearance and decided to add her to the opening credits. The producers of the film hired Debbie Harry to sing Conti and Leeson's song, but she quit when the producers refused to allow her band Blondie to write and perform an original song for the film. Blondie's rejected song, titled "For Your Eyes Only," can be found on their 1982 album, *The Hunter*.

## Release and reception

*For Your Eyes Only* was premiered at the Odeon Leicester Square in London on Template:Nowrap, setting an all-time opening-day record for any film at any cinema in the UK with a gross of £14,998 (£Template:Inflation in Template:Inflation-year poundsTemplate:Inflation-fn). The film entered general release in the UK the same day. *For Your Eyes Only* had its North American premiere in Canada and the US on Friday 26 June, at approximately 1,100 cinemas.

 File:FYEO Watch.jpg
The film grossed $54.8 million in the United States, (equivalent to $101.5 million at 2011 ticket prices or $Template:Inflation million in Template:Inflation-year dollars,Template:Inflation-fn adjusted for general inflation) and $195.3 million worldwide,**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** becoming the second highest grossing Bond film after its predecessor, *Moonraker*. This was the last James Bond film to be solely released by United Artists, as by this time its owner, Transamerica Corporation, finalized the sale of the company to MGM. Following the MGM and United Artists merger, later runs including future entries were released under "MGM/UA Distribution Co".

The promotional cinema poster for the film featured a woman holding a crossbow; she was photographed from behind, and her outfit left the bottom half of her buttocks exposed. The effect was achieved by having the model wear a pair of bikini bottoms backwards, so that the part seen on her backside is the front of the suit.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** The poster caused some furor—largely in the US—with *The Boston Globe* and the *Los Angeles Times* considering the poster so unsuitable they edited out everything above the knee, whilst the *Pittsburgh Press* editors painted a pair of shorts over the legs.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** There was significant speculation as to the identity of the model before photographer Morgan Kane identified her as Joyce Bartle.

A number of items of merchandising were issued to coincide with the film, including a 007 digital watch and a copy of Melina's Citroën 2CV by Corgi Toys.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Citroën itself produced a special "007" edition of the 2CV, which even had decorative bullet holes on the door. Marvel Comics also did a comic book adaptation (see section below).**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

### Contemporary reviews

Derek Malcolm in *The Guardian* disliked the film, saying it was "too long ... and pretty boring between the stunts", although he admitted that the stunts were of a high quality. According to Malcolm, Bond "inhabits a fantasy-land of more or less bloodless violence, groinless sex and naivety masked as superior sophistication", with Moore playing him as if in a "nicely lubricated daze". Although Malcolm tipped the film for international box office success, he observed that he "can't quite see why the series has lasted so long and so strong in people's affections." Writing in *The Observer*, Philip French commented that "not for the first time the pre-credits sequence is the best thing about the film." French was dismissive of Moore's Bond, saying that Bond was "impersonated by Moore" and referred to Moore's advancing years.

David Robinson, writing in *The Times* bemoaned the fact that the "dramatic bits between the set pieces don't count for much." Like other critics at the time his praise was more directed towards the stunt crews; they were "better than ever in this one." The film critic for the magazine *Time Out* was brief and pithy: "no plot and poor dialogue, and Moore really is old enough to be the uncle of those girls."

For the US press, Gary Arnold in *The Washington Post* thought the film was "undeniably easy on the eyes", and further added "maybe too easy to prevent the mind from wandering and the lids from drooping." Arnold was also critical of the large set pieces, calling them "more ponderous than sensational" and that there was "no equivalent of the classic action highlights that can be recalled readily from *From Russia with Love* or *You Only Live Twice* or *The Spy Who Loved Me* or *Moonraker*. This is a Bond waiting for something inspired to push it over the top." *The New York Times* critic Vincent Canby said that "*For Your Eyes Only* is not the best of the series by a long shot" although he did say that the film is "slick entertainment" with a tone that is "consistently comic even when the material is not."

Jack Kroll in *Newsweek* dismissed the film, saying it was "an anthology of action episodes held together by the thinnest of plot lines", although he did concede that these set pieces are "terrific in their exhilaratingly absurd energy." For *Time* magazine, Richard Corliss concentrated on the stunts, saying the team "have devised some splendid optional features for *For Your Eyes Only*" whilst also commenting on Roger Moore, saying that his "mannequin good looks and waxed-fruit insouciance" show him to be "the best-oiled cog in this perpetual motion machine." Jay Scott of *The Globe and Mail* included it on his list of the year's worst films, calling it "repellant" and "ambitiously bad".

French filmmaker Robert Bresson admired the film: "It filled me with wonder because of its cinematographic writing ... if I could have seen it twice in a row and again the next day, I would have done." Elsewhere Bresson said he also loved the film's ski chase.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

### Retrospective reviews

Opinion on *For Your Eyes Only* has improved with the passing of time, though some reviews are still mixed to positive: Template:As of, the film holds a 73% 'fresh' rating from Rotten Tomatoes, being ranked eleventh among the 24 Bond films. Ian Nathan of *Empire* gives the film only two of a possible five stars, observing that the film "still ranks as one of the most forgettable Bonds on record." In 2006, IGN chose *For Your Eyes Only* as the sixth best Bond film, claiming it is "a good old-fashioned espionage tale", a placement shared by Norman Wilner of MSN, who considered it "the one Moore film that seems to reach back to Connery's heyday", and *Entertainment Weekly* chose it as the tenth best in 2008, saying it was a "return to low-tech, low-key Bond [with] ... some of the best stunts since the early days". In October 2008 *Time Out* re-issued a review of *For Your Eyes Only* and observed that the film is "admirable in intent" but that it "feels a little spare", largely because the plot has been "divested of the bells and whistles that hallmark the franchise".

James Berardinelli wrote that the film was "a solid adventure, although it could have been better", while Danny Peary thought "There are exciting moments, but most of it is standard Bond fare," going on to describe *For Your Eyes Only* as "an attempt to mix spectacle with [the] tough, believable storylines of early Bond films ... [it] is enjoyable while you're watching it. Afterward, it's one of the most forgettable of the Bond series."**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** Raymond Benson, the author of nine Bond novels, thought *For Your Eyes Only* was Roger Moore's best Bond film.

Although Chris Nashawaty of *Entertainment Weekly* ranks Carole Bouquet playing Melina as the "worst babe" of the seven Roger Moore James Bond films, his colleague, Joshua Rich disagreed, putting her tenth in the overall 10 Best Bond Girls listing from the 21 films released up to that point. *Entertainment Weekly* also ranked Lynn-Holly Johnson as Bibi Dahl as ninth on their list of the 10 worst Bond girls from the 21 films that had been released. After 20 films had been released, IGN ranked Bouquet as fifth in their 'top 10 Bond Babes' list, and *The Times* thought she was sixth on their list of the Top 10 most fashionable Bond girls after 21 films had been released.

### Accolades

The title song *For Your Eyes Only* performed by singer Sheena Easton was nominated for a Best Original Song at the 39th Golden Globe Awards and Best Original Song at the 1981 Academy Awards, losing out at both ceremonies to "Arthur's Theme" from the film *Arthur*. However, the 1981 Academy Awards ceremony did see the presenting of the Irving G. Thalberg Memorial Award to producer Albert R. Broccoli. The Writers Guild of America nominated the script by Michael G. Wilson and Richard Maibaum for Best Adapted Screenplay – Comedy or Musical Picture. In 2004 the American Film Institute nominated the song "For Your Eyes Only" from the film for AFI's 100 Years...100 Songs.

## Adaptations

 File:FYEOcomicbook.jpg
*Main article: James Bond comic books*
As part of the merchandising of *For Your Eyes Only*, Marvel Comics published an adaptation of the movie as issue 19 of *Marvel Comics Super Special*; this was also repackaged as a two-issue comic book adaptation of the film.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).****Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).** The first issue was released in October 1981 and was soon followed by the second issue in November of the same year. The adaptation was written by Larry Hama, pencilled by Howard Chaykin, inked by Vincent Colletta, and edited by Dennis O'Neil.**Lua error in Module:Footnotes at line 53: attempt to index local 'anchor\_id\_list' (a nil value).**

It was the second film in the series to have a comic book tie-in, following a *Dr. No* comic in 1962. Marvel Comics would go on to publish an *Octopussy* comic book adaptation in 1983.

In a 2015 interview with CBC Radio One Lee Demarbre and Ian Driscoll announced their plans to make a Canadian film adaptation of *For Your Eyes Only* set in Ottawa, which they intend to be the first instalment in a four-film series. The production of a film is possible because Canada enforces the Berne Convention and the Copyright Act automatically releasing an author's entire collected works to the public domain 50 years after their death, causing Ian Fleming's works to enter the public domain in Canada in 2014. Demarbre and Driscoll expressed interest in casting Ryan Reynolds as James Bond; Jessica Paré as Judy Havelock; Donald Sutherland, Christopher Plummer, and Michael Ironside as supporting characters; and the Dead Obies as the performers of the title song. Although a film adaptation will not be releasable in the United States and Europe due to differing copyright law they believed it could recoup its profits through a release in Asia, including China.

## References

1. ↑ For Your Eyes Only. European Audiovisual Observatory.
2. ↑ Moses, Antoinette. Sight and Sound; London Vol. 51, Iss. 4, (Fall 1982): 258.
3. ↑ Template:Cite video
4. ↑ Brady, Shirley. Good Skate. *People*.
5. ↑ 5.0 5.1 5.2 5.3 5.4 5.5 Field, Matthew (2015). *Some kind of hero : 007 : the remarkable story of the James Bond films*. ISBN 978-0-7509-6421-0.
6. ↑ /.
7. ↑  (2008) *My Word is My Bond: A Memoir*. London: Michael O’Mara Publications. ISBN 9780732288716.
8. ↑ Hibbin, Sally (1989). *The making of Licence to kill*. Salem House, page 14. ISBN 978-0-88162-453-3.
9. ↑ 9.0 9.1 For Your Eyes Only.
10. ↑ 10.0 10.1 Another Interview With: Christopher Wood) @ Universal Exports, The Home of James Bond, 007.
11. ↑ Rothman, Lily. "'Moonraker' Was Made Because of 'Star Wars' | James Bond, Declassified: 50 Things You Didn't Know About 007", *Time*, 25 September 2012. (in en-US)
12. ↑ 12.00 12.01 12.02 12.03 12.04 12.05 12.06 12.07 12.08 12.09 12.10 12.11 12.12 12.13 12.14 12.15 Template:Cite video
13. ↑ Template:Cite video
14. ↑ Duncan, Paul (2012). *The James Bond Archives*. Taschen, page 305.
15. ↑ Boshoff, Alison. "Double agent Bond held hostage in studio wars", 22 October 1997.
16. ↑ Template:Cite magazine
17. ↑ Roberts, Andrew. "The Bond bunch", 8 November 2006.
18. ↑ 18.0 18.1 MI6 :: The Home of James Bond.
19. ↑ Solomans, Jason. "My kingdom for a Bond: More than 70 contenders and still no new 007", 17 April 2005.
20. ↑ Heritage, Stuart (6 April 2014). For Your Eyes Only recap: just when Bond show his soft side, out come the guns.
21. ↑ Timothy Dalton Chat Group.
22. ↑ For Your Eyes Only.
23. ↑ ROGER MOORE BOWS OUT. (1980, Jul 08). *Los Angeles Times*
24. ↑ Pictures: Broccoli confirms moore as 007 in 'eyes' pic. (1980, Jul 23). *Variety, 299*, 31.
25. ↑ 25.0 25.1 25.2 Template:Cite video
26. ↑ 26.0 26.1 Glen, John (August 1981). "The director talks about For Your Eyes Only". *American Cinematographer*. http://www.ianfleming.org/mkkbb/magazine/81acm34.shtml.
27. ↑ Template:Cite video
28. ↑ 28.0 28.1 Template:Cite video
29. ↑ Template:Cite video
30. ↑ Lightman, Herb A. (August 1981). "Behind the scenes of James Bond 007: For Your Eyes Only". *American Cinematographer*. http://ianfleming.org/mkkbb/magazine/81acm12.shtml.
31. ↑ Samuelson, David (August 1981). "For Your Eyes Only and Its Special Effects". *American Cinematographer*. http://www.ianfleming.org/mkkbb/magazine/81acm34.shtml.
32. ↑ Template:Cite episode
33. ↑ 33.0 33.1 Template:Cite video
34. ↑ 34.0 34.1 Template:Cite video
35. ↑ Template:Cite video
36. ↑ 36.0 36.1 Template:Cite video
37. ↑ Template:Cite video
38. ↑ 38.0 38.1 For Your Eyes Only (Original Motion Picture Soundtrack). Rovi Corporation.
39. ↑ 39.0 39.1 Lee Davies, Adam. 'For Your Eyes Only' revisited. Time Out.
40. ↑ Template:Cite video
41. ↑ Kielty, Martin (March 13, 2020). Why James Bond Rejected Blondie's Version of 'For Your Eyes Only' (en).
42. ↑ O'Brien, Glenn (January 1986). "Debbie's Back". *Spin* **1** (9): 49. Template:Citation/identifier. https://books.google.com/books?id=O8JuDPDsl1gC&pg=PA49. Retrieved 23 September 2016.
43. ↑ "Premieres", 27 June 1981.
44. ↑ 44.0 44.1 44.2 "DATELINE: London, June 26", 26 June 1981.
45. ↑ 45.0 45.1 Box Office History for James Bond Movies. *The Numbers*. Nash Information Services, LLC..
46. ↑ All Time Highest Grossing Movies in the United States Since 1977, Inflation Adjusted. *The Numbers*. Nash Information Services, LLC..
47. ↑ 47.0 47.1 Template:Cite magazine
48. ↑ "The 10 worst cars driven by James Bond: Citroën 2CV (For Your Eyes Only)", MSN, 10 November 2010.
49. ↑ 49.0 49.1 49.2 Malcolm, Derek. "The Bond wagon rolls on", 25 June 1981.
50. ↑ 50.0 50.1 French, Philip. "Bergman and Bond", 28 June 1981.
51. ↑ Robinson, David. "Bergman's inimitable view of human relationships", 26 June 1981.
52. ↑ For Your Eyes Only (1981). *From Time Out Film Guide*. Time Out.
53. ↑ 53.0 53.1 Arnold, Gary. "'Your Eyes Only': Smooth but Lacking", 26 June 1981.
54. ↑ Canby, Vincent. "Bond in 'For Your Eyes Only'", 26 June 1981.
55. ↑ Kroll, Jack. "Down to 001", 29 June 1981.
56. ↑ Template:Cite magazine
57. ↑ 57.0 57.1 Scott, Jay. "Something old, but not much new Abel Gance's aged Napoleon one shimmering superstar in a year of gloomy omens", 2 January 1982, p. E.3.
58. ↑ Scott, Jay. "Plenty Moore of the same 007 has lost a few steps but is still in the running", 10 June 1983, p. E.1.
59. ↑ Bresson in Germany (11 May 2010).The interview may be seen on New Yorker Films' DVD release of Bresson's film *L'Argent*.
60. ↑ Robert Bresson (July 2005). "Chatterbox: Armond White & Robert Bresson". *Film Comment* **41** (4): 8.
61. ↑ For Your Eyes Only. *Rotten Tomatoes*.
62. ↑ In 2008 --[[ ----------------------------------

Lua module implementing the Template loop detected: Template:Webarchive template.

A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives

]]

--[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------
]]

require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;

--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]

local categories = {}; -- category names
local config = {}; -- global configuration settings
local digits = {}; -- for i18n; table that translates local-wiki digits to western digits
local err\_warn\_msgs = {}; -- error and warning messages
local excepted\_pages = {};
local month\_num = {}; -- for i18n; table that translates local-wiki month names to western digits
local prefixes = {}; -- service provider tail string prefixes
local services = {}; -- archive service provider data from
local s\_text = {}; -- table of static text strings used to build final rendering
local uncategorized\_namespaces = {}; -- list of namespaces that we should not categorize
local uncategorized\_subpages = {}; -- list of subpages that should not be categorized

--[[--------------------------< P A G E S C O P E I D E N T I F I E R S >----------------------------------
]]

local non\_western\_digits; -- boolean flag set true when data.digits.enable is true
local this\_page = mw.title.getCurrentTitle();

local track = {}; -- Associative array to hold tracking categories
local ulx = {}; -- Associative array to hold template data

--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------

Populates numbered arguments in a message string using an argument table.

]]

local function substitute (msg, args)
 return args and mw.message.newRawMessage (msg, args):plain() or msg;
end

--[[--------------------------< tableLength >-----------------------

Given a 1-D table, return number of elements

]]

local function tableLength(T)
 local count = 0
 for \_ in pairs(T) do count = count + 1 end
 return count
end

--[=[-------------------------< M A K E \_ W I K I L I N K >----------------------------------------------------

Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only
link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an
empty string.

]=]

local function make\_wikilink (link, display, no\_link)
 if nil == no\_link then
 if link and ( *~= link) then*
 if display and ( *~= display) then*
 return table.concat ({'', display, ''});
 else
 return table.concat ({'', link, ''});
 end
 end
 return display or *; -- link not set so return the display text*

 else -- no\_link
 if display and ( *~= display) then -- if there is display text*
 return display; -- return that
 else
 return link or *; -- return the target article name or empty string*
 end
 end
end

--[[--------------------------< createTracking >-----------------------

Return data in track[] ie. tracking categories

]]

local function createTracking()
 if not excepted\_pages[this\_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s))
 if uncategorized\_namespaces[this\_page.nsText] then
 return *; -- this page not to be categorized so return empty string*
 end
 for \_,v in ipairs (uncategorized\_subpages) do -- cycle through page name patterns
 if this\_page.text:match (v) then -- test page name against each pattern
 return *; -- this subpage type not to be categorized so return empty string*
 end
 end
 end

 local out = {};
 if tableLength(track) > 0 then
 for key, \_ in pairs(track) do -- loop through table
 table.insert (out, make\_wikilink (key)); -- and convert category names to links
 end
 end
 return table.concat (out); -- concat into one big string; empty string if table is empty

end

--[[--------------------------< inlineError >-----------------------

Critical error. Render output completely in red. Add to tracking category.

This function called as the last thing before abandoning this module

]]

local function inlineError (msg, args)
 track[categories.error] = 1
 return table.concat ({
 'Error in ', -- open the error message span
 config.tname, -- insert the local language template name
 ' template: ',
 substitute (msg, args), -- insert the formatted error message
 '.', -- close the span
 createTracking() -- add the category
 })
end

--[[--------------------------< inlineRed >-----------------------

Render a text fragment in red, such as a warning as part of the final output.
Add tracking category.

 ]]

local function inlineRed(msg, trackmsg)
 if trackmsg == "warning" then
 track[categories.warning] = 1;
 elseif trackmsg == "error" then
 track[categories.error] = 1;
 end

 return '' .. msg .. ''
end

--[[--------------------------< base62 >-----------------------

Convert base-62 to base-10
Credit: https://de.wikipedia.org/wiki/Modul:Expr

]]

local function base62( value )
 local r = 1 -- default return value is input value is malformed

 if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z]
 return; -- nil return when value contains extraneous characters
 end

 local n = #value -- number of characters in value
 local k = 1
 local c
 r = 0
 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit
 c = value:byte( i, i )
 if c >= 48 and c <= 57 then -- character is digit 0-9
 c = c - 48
 elseif c >= 65 and c <= 90 then -- character is ascii a-z
 c = c - 55
 else -- must be ascii A-Z
 c = c - 61
 end
 r = r + c \* k -- accumulate this base62 character's value
 k = k \* 62 -- bump for next
 end -- for i

 return r
end

--[[--------------------------< D E C O D E \_ D A T E >--------------------------------------------------------

Given a date string, return it in iso format along with an indicator of the date's format. Except that month names
must be recognizable as legitimate month names with proper capitalization, and that the date string must match one
of the recognized date formats, no error checking is done here; return nil else

]]

local function decode\_date (date\_str)
 local patterns = {
 ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages
 ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'},
 ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis
 };

 local t = {};

 if non\_western\_digits then -- this wiki uses non-western digits?
 date\_str = mw.ustring.gsub (date\_str, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 if date\_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form
 return date\_str, 'iso';
 end

 for k, v in pairs (patterns) do
 local c1, c2, c3 = mw.ustring.match (date\_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold

 if c1 then -- set on match
 t = { -- translate unspecified captures to y, m, and d
 [patterns[k][2]] = c1, -- fill the table of captures with the captures
 [patterns[k][3]] = c2, -- take index names from src\_pattern table and assign sequential captures
 [patterns[k][4]] = c3,
 };
 if month\_num[t.m] then -- when month not already a number
 t.m = month\_num[t.m]; -- replace valid month name with a number
 else
 return nil, 'iso'; -- not a valid date form because month not valid
 end

 return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format
 end
 end
 return nil, 'iso'; -- date could not be decoded; return nil and default iso date
end

--[[--------------------------< makeDate >-----------------------

Given year, month, day numbers, (zero-padded or not) return a full date in df format
where df may be one of:
 mdy, dmy, iso, ymd

on entry, year, month, day are presumed to be correct for the date that they represent; all are required

in this module, makeDate() is sometimes given an iso-format date in year:
 makeDate (2018-09-20, nil, nil, df)
this works because table.concat() sees only one table member

]]

local function makeDate (year, month, day, df)
 local format = {
 ['dmy'] = 'j F Y',
 ['mdy'] = 'F j, Y',
 ['ymd'] = 'Y F j',
 ['iso'] = 'Y-m-d',
 };

 local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here)

 if non\_western\_digits then -- this wiki uses non-western digits?
 date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return mw.getContentLanguage():formatDate (format[df], date);
end

--[[--------------------------< I S \_ V A L I D \_ D A T E >----------------------------------------------------

Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth
be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies
Gregorian leapyear rules.

all arguments are required

]]

local function is\_valid\_date (year, month, day)
 local days\_in\_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 local month\_length;
 local y, m, d;
 local today = os.date ('\*t'); -- fetch a table of current date parts

 if not year or  *== year or not month or*  == month or not day or  *== day then*
 return false; -- something missing
 end

 y = tonumber (year);
 m = tonumber (month);
 d = tonumber (day);

 if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900?
 return false;
 end

 if (2==m) then -- if February
 month\_length = 28; -- then 28 days unless
 if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year?
 month\_length = 29; -- if leap year then 29 days in February
 end
 else
 month\_length=days\_in\_month[m];
 end

 if 1 > d or month\_length < d then -- day is within bounds
 return false;
 end
 -- here when date parts represent a valid date
 return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time
end

--[[--------------------------< decodeWebciteDate >-----------------------

Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format

returns date string in df format - webcite date is a unix timestamp encoded as bae62
or the string 'query'

]]

local function decodeWebciteDate(path, df)

 local dt = {};
 local decode;

 dt = mw.text.split(path, "/")

 -- valid URL formats that are not base62

 -- http://www.webcitation.org/query?id=1138911916587475
 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03
 -- http://www.webcitation.org/1138911916587475
 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e
 -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e

 if dt[2]:find ('query', 1, true) or
 dt[2]:find ('cache', 1, true) or
 dt[2]:find ('getfile', 1, true) or
 tonumber(dt[2]) then
 return 'query';
 end

 decode = base62(dt[2]); -- base62 string -> exponential number
 if not decode then
 return nil; -- nil return when dt[2] contains characters not in %w
 end
 dt = os.date('\*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts

 decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits
 if non\_western\_digits then -- this wiki uses non-western digits?
 decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return decode;
end

--[[--------------------------< decodeWaybackDate >-----------------------

Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com )
or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com)
or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com)

return the formatted date eg. "September 1, 2016" in df format
Handle non-digits in snapshot ID such as "re\_" and "-" and "\*"

 valid date string in df format - wayback date is valid (including the text string 'index' when date is '/\*/')
 empty string - wayback date is malformed (less than 8 digits, not a valid date)
 nil - wayback date is '/save/' or otherwise not a number

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeWaybackDate(path, df)

 local msg, snapdate;

 snapdate = path:gsub ('^/web/', *):gsub ('^/all/',* ):gsub ('^/ukgwa/', *):gsub ('^/tna/',* ):gsub ('^/', *); -- remove leading /web/, /all/, /ukgwa/, /tna/, or /*
 snapdate = snapdate:match ('^[^/]+'); -- get timestamp
 if snapdate == "\*" then -- eg. /web/\*/http.., etc.
 return 'index'; -- return indicator that this url has an index date
 end

 snapdate = snapdate:gsub ('%a%a\_%d?$', *):gsub ('%-',* ); -- from date, remove any trailing "re\_", dashes

 msg = *;*
 if snapdate:match ('%\*$') then -- a trailing '\*' causes calendar display at archive .org
 snapdate = snapdate:gsub ('%\*$', *); -- remove so not part of length calc later*
 msg = inlineRed (err\_warn\_msgs.ts\_cal, 'warning'); -- make a message
 end

 if not tonumber(snapdate) then
 return nil, 'ts\_nan'; -- return nil (fatal error flag) and message selector
 end

 local dlen = snapdate:len();
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate, msg; -- return date with message if any
 else
 return snapdate, msg .. inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message(s)
 end
end

--[[--------------------------< decodeArchiveisDate >-----------------------

Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com)
return the date in df format (e.g. if df = dmy, return 28 August 2016)
Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552

 valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form)
 empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date)
 nil - wayback date is '/save/'

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeArchiveisDate(path, df)
 local snapdate

 if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else
 return "short link" -- e.g. http://archive.is/hD1qz
 end

 snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', *); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens*

 local dlen = string.len(snapdate)
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate; -- return date
 else
 return snapdate, inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message
 end
 end

--[[--------------------------< serviceName >-----------------------

Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID

]]

local function serviceName(host, no\_link)
 local tracking;
 local index;

 host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains

 if services[host] then
 index = host;
 else
 for k, \_ in pairs (services) do
 if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then
 index = k;
 break;
 end
 end
 end

 if index then
 local out = {*}; -- empty string in [1] so that concatenated result has leading single space*
 ulx.url1.service = services[index][4] or 'other';
 tracking = services[index][5] or categories.other;
 -- build tail string
 if false == services[index][1] then -- select prefix
 table.insert (out, prefixes.at);
 elseif true == services[index][1] then
 table.insert (out, prefixes.atthe);
 else
 table.insert (out, services[index][1]);
 end

 table.insert (out, make\_wikilink (services[index][2], services[index][3], no\_link)); -- add article wikilink
 if services[index][6] then -- add tail postfix if it exists
 table.insert (out, services[index][6]);
 end

 ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character

 else -- here when unknown archive
 ulx.url1.service = 'other';
 tracking = categories.unknown;
 ulx.url1.tail = table.concat ({*, prefixes.at, host, inlineRed (err\_warn\_msgs.unknown\_url, error)}, ' ');*
 end

 track[tracking] = 1
end

--[[--------------------------< parseExtraArgs >-----------------------

Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10
 For example: Template loop detected: Template:Webarchive
 Three url arguments not in numeric sequence (1..4..7).
 Function only processes arguments numbered 2 or greater (in this case 4 and 7)
 It creates numeric sequenced table entries like:
 urlx.url2.url = <argument value for url4>
 urlx.url3.url = <argument value for url7>
 Returns the number of URL arguments found numbered 2 or greater (in this case returns "2")

 ]]

local function parseExtraArgs(args)

 local i, j, argurl, argurl2, argdate, argtitle

 j = 2
 for i = 2, config.maxurls do
 argurl = "url" .. i
 if args[argurl] then
 argurl2 = "url" .. j
 ulx[argurl2] = {}
 ulx[argurl2]["url"] = args[argurl]
 argdate = "date" .. i
 if args[argdate] then
 ulx[argurl2]["date"] = args[argdate]
 else
 ulx[argurl2]["date"] = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end

 argtitle = "title" .. i
 if args[argtitle] then
 ulx[argurl2]["title"] = args[argtitle]
 else
 ulx[argurl2]["title"] = nil
 end
 j = j + 1
 end
 end

 if j == 2 then
 return 0
 else
 return j - 2
 end
end

--[[--------------------------< comma >-----------------------

Given a date string, return "," if it's MDY

]]

local function comma(date)
 return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or *;*
end

--[[--------------------------< createRendering >-----------------------

Return a rendering of the data in ulx[][]

]]

local function createRendering()

 local displayfield
 local out = {};

 local index\_date, msg = ulx.url1.date:match ('(index)(.\*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message)
 ulx.url1.date = ulx.url1.date:gsub ('index.\*', 'index'); -- remove message

 if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx.url1.url); -- add url

 if ulx.url1.title then
 table.insert (out, ' ') -- the required space
 table.insert (out, ulx.url1.title) -- the title
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, ulx.url1.tail); -- tail text
 if ulx.url1.date then
 table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space?
 table.insert (out, 'index' == ulx.url1.date and s\_text.archive or s\_text.archived); -- add text
 table.insert (out, ' '); -- insert a space
 table.insert (out, ulx.url1.date); -- add date
 table.insert (out, ')'); -- close date text
 end
 else -- no title
 if index\_date then -- when url date is 'index'
 table.insert (out, table.concat ({' ', s\_text.Archive\_index, ']'})); -- add the index link label
 table.insert (out, msg or *); -- add date mismatch message when url date is /\*/ and |date= has valid date*
 else
 table.insert (out, table.concat ({' ', s\_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one)
 end
 if ulx.url1.date then
 if 'index' ~= ulx.url1.date then
 table.insert (out, ulx.url1.date); -- add date when data is not 'index'
 end
 table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy
 table.insert (out, ulx.url1.tail); -- add tail text
 else -- no date
 table.insert (out, ulx.url1.tail); -- add tail text
 end
 end

 if 0 < ulx.url1.extraurls then -- For multiple archive URLs
 local tot = ulx.url1.extraurls + 1
 table.insert (out, '.') -- terminate first url
 table.insert (out, table.concat ({' ', s\_text.addlarchives, ': '})); -- add header text

 for i=2, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add the url
 table.insert (out, ' '); -- the required space
 table.insert (out, ulx[index][displayfield]); -- add the label
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, i==tot and '.' or ', '); -- add terminator
 end
 end
 return table.concat (out); -- make a big string and done

 else -- For Template:Cite archives
 if 'addlarchives' == ulx.url1.format then -- Multiple archive services
 table.insert (out, table.concat ({s\_text.addlarchives, ': '})); -- add header text
 else -- Multiple pages from the same archive
 table.insert (out, table.concat ({s\_text.addlpages, ' '})); -- add header text
 table.insert (out, ulx.url1.date); -- add date to header text
 table.insert (out, ': '); -- close header text
 end

 local tot = ulx.url1.extraurls + 1;
 for i=1, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add url
 table.insert (out, ' '); -- add required space

 displayfield = ulx[index]['title'];
 if 'addlarchives' == ulx.url1.format then
 if not displayfield then
 displayfield = ulx[index]['date']
 end
 else -- must be addlpages
 if not displayfield then
 displayfield = table.concat ({s\_text.Page, ' ', i});
 end
 end
 table.insert (out, displayfield); -- add title, date, page label text
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, (i==tot and '.' or ', ')); -- add terminator
 end
 return table.concat (out); -- make a big string and done
 end
end

--[[--------------------------< P A R A M E T E R \_ N A M E \_ X L A T E >--------------------------------------

for internaltionalization, translate local-language parameter names to their English equivalents

TODO: return error message if multiple aliases of the same canonical parameter name are found?

returns two tables:
 new\_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form
 origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language

unrecognized parameters are ignored

]]

local function parameter\_name\_xlate (args, params, enum\_params)
 local name; -- holds modifiable name of the parameter name during evaluation
 local enum; -- for enumerated parameters, holds the enumerator during evaluation
 local found = false; -- flag used to break out of nested for loops
 local new\_args = {}; -- a table that holds canonical and translated parameter k/v pairs
 local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging
 local unnamed\_params; -- set true when unsupported positional parameters are detected

 for k, v in pairs (args) do -- loop through all of the arguments in the args table
 name = k; -- copy of original parameter name

 if 'string' == type (k) then
 if non\_western\_digits then -- true when non-western digits supported at this wiki
 name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else

 if not enum then -- no enumerator so looking for non-enumnerated parameters
 -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (params) do -- loop through each parameter the params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 else -- enumerated parameters
 name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search
 -- TODO: insert shortcut here? if num\_params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (enum\_params) do -- loop through each parameter the num\_params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 end
 else
 unnamed\_params = true; -- flag for unsupported positional parameters
 end
 end -- for k, v
 return new\_args, origin, unnamed\_params;
end

--[[--------------------------< W E B A R C H I V E >----------------------------------------------------------

template entry point

]]

local function webarchive(frame)
 local args = getArgs (frame);

 local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live
 'Module:Webarchive/data',
 frame:getTitle():find('sandbox', 1, true) and '/sandbox' or  *-- this instance is ./sandbox then append /sandbox*
 }));
 categories = data.categories; -- fill in the forward declarations
 config = data.config;
 if data.digits.enable then
 digits = data.digits; -- for i18n; table of digits in the local wiki's language
 non\_western\_digits = true; -- use\_non\_western\_digits
 end
 err\_warn\_msgs = data.err\_warn\_msgs;
 excepted\_pages = data.excepted\_pages;
 month\_num = data.month\_num; -- for i18n; table of month names in the local wiki's language
 prefixes = data.prefixes;
 services = data.services;
 s\_text = data.s\_text;
 uncategorized\_namespaces = data.uncategorized\_namespaces;
 uncategorized\_subpages = data.uncategorized\_subpages;

 local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used
 local unnamed\_params; -- boolean set to true when template call has unnamed parameters
 args, origin, unnamed\_params = parameter\_name\_xlate (args, data.params, data.enum\_params); -- translate parameter names in args to English

 local date, format, msg, udate, uri, url;
 local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format

 if args.url and args.url1 then -- URL argument (first)
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.url, origin.url1});
 end

 url = args.url or args.url1;

 if not url then
 return inlineError (data.crit\_err\_msgs.empty);
 end
 -- these iabot bugs perportedly fixed; removing these causes lua script error
--[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests
 if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot1);
 end
 if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot2);
 end
]]

 if not (url:lower():find ('^http') or url:find ('^//')) then
 return inlineError (data.crit\_err\_msgs.invalid\_url );
 end

 ulx.url1 = {}
 ulx.url1.url = url

 ulx.url1.extraurls = parseExtraArgs(args)

 local good = false;
 good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed

 if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed
 return inlineError (data.crit\_err\_msgs.invalid\_url);
 end

 serviceName(uri.host, args.nolink)

 if args.date and args.date1 then -- Date argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.date, origin.date1});
 end

 date = args.date or args.date1;
 date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space

 if date and config.verifydates then
 if '\*' == date then
 date = 'index';
 ldf = 'iso'; -- set to default format
 elseif 'mdy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'mdy'; -- then |date=mdy overrides iso
 elseif 'dmy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'dmy'; -- then |date=dmy overrides iso
 elseif 'ymd' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'ymd'; -- then |date=ymd overrides iso
 else
 date, ldf = decode\_date (date); -- get an iso format date from date and get date's original format
 end
 end

 if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is \*
 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 else -- no |date=
 udate, msg = decodeWaybackDate (uri.path);

 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 elseif 'webcite' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate = decodeWebciteDate (uri.path); -- get the url date in iso format
 if 'query' ~= udate then -- skip if query
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'),
 });
 end
 end
 end
 end
 else
 date = decodeWebciteDate( uri.path, "iso" )
 if date == "query" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif not date then -- invalid base62 string
 date = inlineRed (err\_warn\_msgs.date1, 'error');
 end
 end

 elseif 'archiveis' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format
 if 'short link' ~= udate then -- skip if short link
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 end
 else -- no |date=
 udate, msg = decodeArchiveisDate( uri.path, "iso" )
 if udate == "short link" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 else -- some other service
 if not date then
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end
 end

 if 'index' == date then
 ulx.url1.date = date .. (msg or *); -- create index + message (if there is one)*
 elseif date then
 ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or *); -- create a date in the wiki's local language + message (if there is one)*
 else
 ulx.url1.date = msg;
 end

 format = args.format; -- Format argument

 if not format then
 format = "none"
 else
 for k, v in pairs (data.format\_vals) do -- |format= accepts two specific values loop through a table of those values
 local found; -- declare a nil flag
 for \_, p in ipairs (v) do -- loop through local language variants
 if format == p then -- when |format= value matches
 format = k; -- use name from table key
 found = true; -- declare found so that we can break out of outer for loop
 break; -- break out of inner for loop
 end
 end

 if found then
 break;
 end
 end

 if format == "addlpages" then
 if not ulx.url1.date then
 format = "none"
 end
 elseif format == "addlarchives" then
 format = "addlarchives"
 else
 format = "none"
 end
 end
 ulx.url1.format = format

 if args.title and args.title1 then -- Title argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.title, origin.title1});
 end

 ulx.url1.title = args.title or args.title1;

 local rend = createRendering()
 if not rend then
 return inlineError (data.crit\_err\_msgs.unknown);
 end

 return rend .. ((unnamed\_params and inlineRed (err\_warn\_msgs.unnamed\_params, 'warning')) or *) .. createTracking();*

end

--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]

return {webarchive = webarchive};, the film was ranked ninth with 73%; ever since the list was posted, *For Your Eyes Only*'s score fell to 69%, falling behind the ones that stood from 12th to 10th --[[ ----------------------------------

Lua module implementing the Template loop detected: Template:Webarchive template.

A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives

]]

--[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------
]]

require('Module:No globals');
local getArgs = require ('Module:Arguments').getArgs;

--[[--------------------------< F O R W A R D D E C L A R A T I O N S >--------------------------------------
]]

local categories = {}; -- category names
local config = {}; -- global configuration settings
local digits = {}; -- for i18n; table that translates local-wiki digits to western digits
local err\_warn\_msgs = {}; -- error and warning messages
local excepted\_pages = {};
local month\_num = {}; -- for i18n; table that translates local-wiki month names to western digits
local prefixes = {}; -- service provider tail string prefixes
local services = {}; -- archive service provider data from
local s\_text = {}; -- table of static text strings used to build final rendering
local uncategorized\_namespaces = {}; -- list of namespaces that we should not categorize
local uncategorized\_subpages = {}; -- list of subpages that should not be categorized

--[[--------------------------< P A G E S C O P E I D E N T I F I E R S >----------------------------------
]]

local non\_western\_digits; -- boolean flag set true when data.digits.enable is true
local this\_page = mw.title.getCurrentTitle();

local track = {}; -- Associative array to hold tracking categories
local ulx = {}; -- Associative array to hold template data

--[[--------------------------< S U B S T I T U T E >----------------------------------------------------------

Populates numbered arguments in a message string using an argument table.

]]

local function substitute (msg, args)
 return args and mw.message.newRawMessage (msg, args):plain() or msg;
end

--[[--------------------------< tableLength >-----------------------

Given a 1-D table, return number of elements

]]

local function tableLength(T)
 local count = 0
 for \_ in pairs(T) do count = count + 1 end
 return count
end

--[=[-------------------------< M A K E \_ W I K I L I N K >----------------------------------------------------

Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only
link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an
empty string.

]=]

local function make\_wikilink (link, display, no\_link)
 if nil == no\_link then
 if link and ( *~= link) then*
 if display and ( *~= display) then*
 return table.concat ({'', display, ''});
 else
 return table.concat ({'', link, ''});
 end
 end
 return display or *; -- link not set so return the display text*

 else -- no\_link
 if display and ( *~= display) then -- if there is display text*
 return display; -- return that
 else
 return link or *; -- return the target article name or empty string*
 end
 end
end

--[[--------------------------< createTracking >-----------------------

Return data in track[] ie. tracking categories

]]

local function createTracking()
 if not excepted\_pages[this\_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s))
 if uncategorized\_namespaces[this\_page.nsText] then
 return *; -- this page not to be categorized so return empty string*
 end
 for \_,v in ipairs (uncategorized\_subpages) do -- cycle through page name patterns
 if this\_page.text:match (v) then -- test page name against each pattern
 return *; -- this subpage type not to be categorized so return empty string*
 end
 end
 end

 local out = {};
 if tableLength(track) > 0 then
 for key, \_ in pairs(track) do -- loop through table
 table.insert (out, make\_wikilink (key)); -- and convert category names to links
 end
 end
 return table.concat (out); -- concat into one big string; empty string if table is empty

end

--[[--------------------------< inlineError >-----------------------

Critical error. Render output completely in red. Add to tracking category.

This function called as the last thing before abandoning this module

]]

local function inlineError (msg, args)
 track[categories.error] = 1
 return table.concat ({
 'Error in ', -- open the error message span
 config.tname, -- insert the local language template name
 ' template: ',
 substitute (msg, args), -- insert the formatted error message
 '.', -- close the span
 createTracking() -- add the category
 })
end

--[[--------------------------< inlineRed >-----------------------

Render a text fragment in red, such as a warning as part of the final output.
Add tracking category.

 ]]

local function inlineRed(msg, trackmsg)
 if trackmsg == "warning" then
 track[categories.warning] = 1;
 elseif trackmsg == "error" then
 track[categories.error] = 1;
 end

 return '' .. msg .. ''
end

--[[--------------------------< base62 >-----------------------

Convert base-62 to base-10
Credit: https://de.wikipedia.org/wiki/Modul:Expr

]]

local function base62( value )
 local r = 1 -- default return value is input value is malformed

 if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z]
 return; -- nil return when value contains extraneous characters
 end

 local n = #value -- number of characters in value
 local k = 1
 local c
 r = 0
 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit
 c = value:byte( i, i )
 if c >= 48 and c <= 57 then -- character is digit 0-9
 c = c - 48
 elseif c >= 65 and c <= 90 then -- character is ascii a-z
 c = c - 55
 else -- must be ascii A-Z
 c = c - 61
 end
 r = r + c \* k -- accumulate this base62 character's value
 k = k \* 62 -- bump for next
 end -- for i

 return r
end

--[[--------------------------< D E C O D E \_ D A T E >--------------------------------------------------------

Given a date string, return it in iso format along with an indicator of the date's format. Except that month names
must be recognizable as legitimate month names with proper capitalization, and that the date string must match one
of the recognized date formats, no error checking is done here; return nil else

]]

local function decode\_date (date\_str)
 local patterns = {
 ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages
 ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'},
 ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis
 };

 local t = {};

 if non\_western\_digits then -- this wiki uses non-western digits?
 date\_str = mw.ustring.gsub (date\_str, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 if date\_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form
 return date\_str, 'iso';
 end

 for k, v in pairs (patterns) do
 local c1, c2, c3 = mw.ustring.match (date\_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold

 if c1 then -- set on match
 t = { -- translate unspecified captures to y, m, and d
 [patterns[k][2]] = c1, -- fill the table of captures with the captures
 [patterns[k][3]] = c2, -- take index names from src\_pattern table and assign sequential captures
 [patterns[k][4]] = c3,
 };
 if month\_num[t.m] then -- when month not already a number
 t.m = month\_num[t.m]; -- replace valid month name with a number
 else
 return nil, 'iso'; -- not a valid date form because month not valid
 end

 return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format
 end
 end
 return nil, 'iso'; -- date could not be decoded; return nil and default iso date
end

--[[--------------------------< makeDate >-----------------------

Given year, month, day numbers, (zero-padded or not) return a full date in df format
where df may be one of:
 mdy, dmy, iso, ymd

on entry, year, month, day are presumed to be correct for the date that they represent; all are required

in this module, makeDate() is sometimes given an iso-format date in year:
 makeDate (2018-09-20, nil, nil, df)
this works because table.concat() sees only one table member

]]

local function makeDate (year, month, day, df)
 local format = {
 ['dmy'] = 'j F Y',
 ['mdy'] = 'F j, Y',
 ['ymd'] = 'Y F j',
 ['iso'] = 'Y-m-d',
 };

 local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here)

 if non\_western\_digits then -- this wiki uses non-western digits?
 date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return mw.getContentLanguage():formatDate (format[df], date);
end

--[[--------------------------< I S \_ V A L I D \_ D A T E >----------------------------------------------------

Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth
be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies
Gregorian leapyear rules.

all arguments are required

]]

local function is\_valid\_date (year, month, day)
 local days\_in\_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 local month\_length;
 local y, m, d;
 local today = os.date ('\*t'); -- fetch a table of current date parts

 if not year or  *== year or not month or*  == month or not day or  *== day then*
 return false; -- something missing
 end

 y = tonumber (year);
 m = tonumber (month);
 d = tonumber (day);

 if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900?
 return false;
 end

 if (2==m) then -- if February
 month\_length = 28; -- then 28 days unless
 if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year?
 month\_length = 29; -- if leap year then 29 days in February
 end
 else
 month\_length=days\_in\_month[m];
 end

 if 1 > d or month\_length < d then -- day is within bounds
 return false;
 end
 -- here when date parts represent a valid date
 return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time
end

--[[--------------------------< decodeWebciteDate >-----------------------

Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format

returns date string in df format - webcite date is a unix timestamp encoded as bae62
or the string 'query'

]]

local function decodeWebciteDate(path, df)

 local dt = {};
 local decode;

 dt = mw.text.split(path, "/")

 -- valid URL formats that are not base62

 -- http://www.webcitation.org/query?id=1138911916587475
 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03
 -- http://www.webcitation.org/1138911916587475
 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e
 -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e

 if dt[2]:find ('query', 1, true) or
 dt[2]:find ('cache', 1, true) or
 dt[2]:find ('getfile', 1, true) or
 tonumber(dt[2]) then
 return 'query';
 end

 decode = base62(dt[2]); -- base62 string -> exponential number
 if not decode then
 return nil; -- nil return when dt[2] contains characters not in %w
 end
 dt = os.date('\*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts

 decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits
 if non\_western\_digits then -- this wiki uses non-western digits?
 decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 return decode;
end

--[[--------------------------< decodeWaybackDate >-----------------------

Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com )
or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com)
or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com)

return the formatted date eg. "September 1, 2016" in df format
Handle non-digits in snapshot ID such as "re\_" and "-" and "\*"

 valid date string in df format - wayback date is valid (including the text string 'index' when date is '/\*/')
 empty string - wayback date is malformed (less than 8 digits, not a valid date)
 nil - wayback date is '/save/' or otherwise not a number

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeWaybackDate(path, df)

 local msg, snapdate;

 snapdate = path:gsub ('^/web/', *):gsub ('^/all/',* ):gsub ('^/ukgwa/', *):gsub ('^/tna/',* ):gsub ('^/', *); -- remove leading /web/, /all/, /ukgwa/, /tna/, or /*
 snapdate = snapdate:match ('^[^/]+'); -- get timestamp
 if snapdate == "\*" then -- eg. /web/\*/http.., etc.
 return 'index'; -- return indicator that this url has an index date
 end

 snapdate = snapdate:gsub ('%a%a\_%d?$', *):gsub ('%-',* ); -- from date, remove any trailing "re\_", dashes

 msg = *;*
 if snapdate:match ('%\*$') then -- a trailing '\*' causes calendar display at archive .org
 snapdate = snapdate:gsub ('%\*$', *); -- remove so not part of length calc later*
 msg = inlineRed (err\_warn\_msgs.ts\_cal, 'warning'); -- make a message
 end

 if not tonumber(snapdate) then
 return nil, 'ts\_nan'; -- return nil (fatal error flag) and message selector
 end

 local dlen = snapdate:len();
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate, msg; -- return date with message if any
 else
 return snapdate, msg .. inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message(s)
 end
end

--[[--------------------------< decodeArchiveisDate >-----------------------

Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com)
return the date in df format (e.g. if df = dmy, return 28 August 2016)
Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552

 valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form)
 empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date)
 nil - wayback date is '/save/'

 second return value is an appropriate 'message' may or may not be formatted

]]

local function decodeArchiveisDate(path, df)
 local snapdate

 if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else
 return "short link" -- e.g. http://archive.is/hD1qz
 end

 snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', *); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens*

 local dlen = string.len(snapdate)
 if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits?
 return *, inlineRed (err\_warn\_msgs.ts\_short, 'error'); -- return empty string and error message*
 end

 local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here

 if not is\_valid\_date (year, month, day) then
 return *, inlineRed (err\_warn\_msgs.ts\_date, 'error'); -- return empty string and error message*
 end

 snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format
 if 14 == dlen then
 return snapdate; -- return date
 else
 return snapdate, inlineRed (err\_warn\_msgs.ts\_len, 'warning'); -- return date with warning message
 end
 end

--[[--------------------------< serviceName >-----------------------

Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID

]]

local function serviceName(host, no\_link)
 local tracking;
 local index;

 host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains

 if services[host] then
 index = host;
 else
 for k, \_ in pairs (services) do
 if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then
 index = k;
 break;
 end
 end
 end

 if index then
 local out = {*}; -- empty string in [1] so that concatenated result has leading single space*
 ulx.url1.service = services[index][4] or 'other';
 tracking = services[index][5] or categories.other;
 -- build tail string
 if false == services[index][1] then -- select prefix
 table.insert (out, prefixes.at);
 elseif true == services[index][1] then
 table.insert (out, prefixes.atthe);
 else
 table.insert (out, services[index][1]);
 end

 table.insert (out, make\_wikilink (services[index][2], services[index][3], no\_link)); -- add article wikilink
 if services[index][6] then -- add tail postfix if it exists
 table.insert (out, services[index][6]);
 end

 ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character

 else -- here when unknown archive
 ulx.url1.service = 'other';
 tracking = categories.unknown;
 ulx.url1.tail = table.concat ({*, prefixes.at, host, inlineRed (err\_warn\_msgs.unknown\_url, error)}, ' ');*
 end

 track[tracking] = 1
end

--[[--------------------------< parseExtraArgs >-----------------------

Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10
 For example: Template loop detected: Template:Webarchive
 Three url arguments not in numeric sequence (1..4..7).
 Function only processes arguments numbered 2 or greater (in this case 4 and 7)
 It creates numeric sequenced table entries like:
 urlx.url2.url = <argument value for url4>
 urlx.url3.url = <argument value for url7>
 Returns the number of URL arguments found numbered 2 or greater (in this case returns "2")

 ]]

local function parseExtraArgs(args)

 local i, j, argurl, argurl2, argdate, argtitle

 j = 2
 for i = 2, config.maxurls do
 argurl = "url" .. i
 if args[argurl] then
 argurl2 = "url" .. j
 ulx[argurl2] = {}
 ulx[argurl2]["url"] = args[argurl]
 argdate = "date" .. i
 if args[argdate] then
 ulx[argurl2]["date"] = args[argdate]
 else
 ulx[argurl2]["date"] = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end

 argtitle = "title" .. i
 if args[argtitle] then
 ulx[argurl2]["title"] = args[argtitle]
 else
 ulx[argurl2]["title"] = nil
 end
 j = j + 1
 end
 end

 if j == 2 then
 return 0
 else
 return j - 2
 end
end

--[[--------------------------< comma >-----------------------

Given a date string, return "," if it's MDY

]]

local function comma(date)
 return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or *;*
end

--[[--------------------------< createRendering >-----------------------

Return a rendering of the data in ulx[][]

]]

local function createRendering()

 local displayfield
 local out = {};

 local index\_date, msg = ulx.url1.date:match ('(index)(.\*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message)
 ulx.url1.date = ulx.url1.date:gsub ('index.\*', 'index'); -- remove message

 if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx.url1.url); -- add url

 if ulx.url1.title then
 table.insert (out, ' ') -- the required space
 table.insert (out, ulx.url1.title) -- the title
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, ulx.url1.tail); -- tail text
 if ulx.url1.date then
 table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space?
 table.insert (out, 'index' == ulx.url1.date and s\_text.archive or s\_text.archived); -- add text
 table.insert (out, ' '); -- insert a space
 table.insert (out, ulx.url1.date); -- add date
 table.insert (out, ')'); -- close date text
 end
 else -- no title
 if index\_date then -- when url date is 'index'
 table.insert (out, table.concat ({' ', s\_text.Archive\_index, ']'})); -- add the index link label
 table.insert (out, msg or *); -- add date mismatch message when url date is /\*/ and |date= has valid date*
 else
 table.insert (out, table.concat ({' ', s\_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one)
 end
 if ulx.url1.date then
 if 'index' ~= ulx.url1.date then
 table.insert (out, ulx.url1.date); -- add date when data is not 'index'
 end
 table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy
 table.insert (out, ulx.url1.tail); -- add tail text
 else -- no date
 table.insert (out, ulx.url1.tail); -- add tail text
 end
 end

 if 0 < ulx.url1.extraurls then -- For multiple archive URLs
 local tot = ulx.url1.extraurls + 1
 table.insert (out, '.') -- terminate first url
 table.insert (out, table.concat ({' ', s\_text.addlarchives, ': '})); -- add header text

 for i=2, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add the url
 table.insert (out, ' '); -- the required space
 table.insert (out, ulx[index][displayfield]); -- add the label
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, i==tot and '.' or ', '); -- add terminator
 end
 end
 return table.concat (out); -- make a big string and done

 else -- For Template:Cite archives
 if 'addlarchives' == ulx.url1.format then -- Multiple archive services
 table.insert (out, table.concat ({s\_text.addlarchives, ': '})); -- add header text
 else -- Multiple pages from the same archive
 table.insert (out, table.concat ({s\_text.addlpages, ' '})); -- add header text
 table.insert (out, ulx.url1.date); -- add date to header text
 table.insert (out, ': '); -- close header text
 end

 local tot = ulx.url1.extraurls + 1;
 for i=1, tot do -- loop through the additionals
 local index = table.concat ({'url', i}); -- make an index
 table.insert (out, '['); -- open extlink markup
 table.insert (out, ulx[index]['url']); -- add url
 table.insert (out, ' '); -- add required space

 displayfield = ulx[index]['title'];
 if 'addlarchives' == ulx.url1.format then
 if not displayfield then
 displayfield = ulx[index]['date']
 end
 else -- must be addlpages
 if not displayfield then
 displayfield = table.concat ({s\_text.Page, ' ', i});
 end
 end
 table.insert (out, displayfield); -- add title, date, page label text
 table.insert (out, ']'); -- close extlink markup
 table.insert (out, (i==tot and '.' or ', ')); -- add terminator
 end
 return table.concat (out); -- make a big string and done
 end
end

--[[--------------------------< P A R A M E T E R \_ N A M E \_ X L A T E >--------------------------------------

for internaltionalization, translate local-language parameter names to their English equivalents

TODO: return error message if multiple aliases of the same canonical parameter name are found?

returns two tables:
 new\_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form
 origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language

unrecognized parameters are ignored

]]

local function parameter\_name\_xlate (args, params, enum\_params)
 local name; -- holds modifiable name of the parameter name during evaluation
 local enum; -- for enumerated parameters, holds the enumerator during evaluation
 local found = false; -- flag used to break out of nested for loops
 local new\_args = {}; -- a table that holds canonical and translated parameter k/v pairs
 local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging
 local unnamed\_params; -- set true when unsupported positional parameters are detected

 for k, v in pairs (args) do -- loop through all of the arguments in the args table
 name = k; -- copy of original parameter name

 if 'string' == type (k) then
 if non\_western\_digits then -- true when non-western digits supported at this wiki
 name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits
 end

 enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else

 if not enum then -- no enumerator so looking for non-enumnerated parameters
 -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (params) do -- loop through each parameter the params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 else -- enumerated parameters
 name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search
 -- TODO: insert shortcut here? if num\_params[name] then name holds the canonical parameter name; no need to search further
 for pname, aliases in pairs (enum\_params) do -- loop through each parameter the num\_params table
 for \_, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table
 if name == alias then
 pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator
 new\_args[pname] = v; -- create a new entry in the new\_args table
 origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name
 found = true; -- flag so that we can break out of these nested for loops
 break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair
 end
 end

 if found then -- true when we found an alias that matched name
 found = false; -- reset the flag
 break; -- go do next args k/v pair
 end
 end
 end
 else
 unnamed\_params = true; -- flag for unsupported positional parameters
 end
 end -- for k, v
 return new\_args, origin, unnamed\_params;
end

--[[--------------------------< W E B A R C H I V E >----------------------------------------------------------

template entry point

]]

local function webarchive(frame)
 local args = getArgs (frame);

 local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live
 'Module:Webarchive/data',
 frame:getTitle():find('sandbox', 1, true) and '/sandbox' or  *-- this instance is ./sandbox then append /sandbox*
 }));
 categories = data.categories; -- fill in the forward declarations
 config = data.config;
 if data.digits.enable then
 digits = data.digits; -- for i18n; table of digits in the local wiki's language
 non\_western\_digits = true; -- use\_non\_western\_digits
 end
 err\_warn\_msgs = data.err\_warn\_msgs;
 excepted\_pages = data.excepted\_pages;
 month\_num = data.month\_num; -- for i18n; table of month names in the local wiki's language
 prefixes = data.prefixes;
 services = data.services;
 s\_text = data.s\_text;
 uncategorized\_namespaces = data.uncategorized\_namespaces;
 uncategorized\_subpages = data.uncategorized\_subpages;

 local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used
 local unnamed\_params; -- boolean set to true when template call has unnamed parameters
 args, origin, unnamed\_params = parameter\_name\_xlate (args, data.params, data.enum\_params); -- translate parameter names in args to English

 local date, format, msg, udate, uri, url;
 local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format

 if args.url and args.url1 then -- URL argument (first)
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.url, origin.url1});
 end

 url = args.url or args.url1;

 if not url then
 return inlineError (data.crit\_err\_msgs.empty);
 end
 -- these iabot bugs perportedly fixed; removing these causes lua script error
--[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests
 if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot1);
 end
 if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred
 track[categories.error] = 1;
 return inlineError (data.crit\_err\_msgs.iabot2);
 end
]]

 if not (url:lower():find ('^http') or url:find ('^//')) then
 return inlineError (data.crit\_err\_msgs.invalid\_url );
 end

 ulx.url1 = {}
 ulx.url1.url = url

 ulx.url1.extraurls = parseExtraArgs(args)

 local good = false;
 good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed

 if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed
 return inlineError (data.crit\_err\_msgs.invalid\_url);
 end

 serviceName(uri.host, args.nolink)

 if args.date and args.date1 then -- Date argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.date, origin.date1});
 end

 date = args.date or args.date1;
 date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space

 if date and config.verifydates then
 if '\*' == date then
 date = 'index';
 ldf = 'iso'; -- set to default format
 elseif 'mdy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'mdy'; -- then |date=mdy overrides iso
 elseif 'dmy' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'dmy'; -- then |date=dmy overrides iso
 elseif 'ymd' == date then
 date = nil; -- if date extracted from URL,
 ldf = 'ymd'; -- then |date=ymd overrides iso
 else
 date, ldf = decode\_date (date); -- get an iso format date from date and get date's original format
 end
 end

 if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is \*
 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 else -- no |date=
 udate, msg = decodeWaybackDate (uri.path);

 if not udate then -- this is the only 'fatal' error return
 return inlineError (data.crit\_err\_msgs[msg]);
 end

 if  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 elseif 'webcite' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate = decodeWebciteDate (uri.path); -- get the url date in iso format
 if 'query' ~= udate then -- skip if query
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'),
 });
 end
 end
 end
 end
 else
 date = decodeWebciteDate( uri.path, "iso" )
 if date == "query" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif not date then -- invalid base62 string
 date = inlineRed (err\_warn\_msgs.date1, 'error');
 end
 end

 elseif 'archiveis' == ulx.url1.service then
 if date then
 if config.verifydates then
 if ldf then
 udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format
 if 'short link' ~= udate then -- skip if short link
 if udate ~= date then -- date comparison using iso format dates
 date = udate;
 msg = table.concat ({
 inlineRed (err\_warn\_msgs.mismatch, 'warning'), -- add warning message
 msg, -- add message if there is one
 });
 end
 end
 end
 end
 else -- no |date=
 udate, msg = decodeArchiveisDate( uri.path, "iso" )
 if udate == "short link" then
 date = nil; -- unset
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 elseif  *== udate then*
 date = nil; -- unset
 else
 date = udate;
 end
 end

 else -- some other service
 if not date then
 msg = inlineRed (err\_warn\_msgs.date\_miss, 'warning');
 end
 end

 if 'index' == date then
 ulx.url1.date = date .. (msg or *); -- create index + message (if there is one)*
 elseif date then
 ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or *); -- create a date in the wiki's local language + message (if there is one)*
 else
 ulx.url1.date = msg;
 end

 format = args.format; -- Format argument

 if not format then
 format = "none"
 else
 for k, v in pairs (data.format\_vals) do -- |format= accepts two specific values loop through a table of those values
 local found; -- declare a nil flag
 for \_, p in ipairs (v) do -- loop through local language variants
 if format == p then -- when |format= value matches
 format = k; -- use name from table key
 found = true; -- declare found so that we can break out of outer for loop
 break; -- break out of inner for loop
 end
 end

 if found then
 break;
 end
 end

 if format == "addlpages" then
 if not ulx.url1.date then
 format = "none"
 end
 elseif format == "addlarchives" then
 format = "addlarchives"
 else
 format = "none"
 end
 end
 ulx.url1.format = format

 if args.title and args.title1 then -- Title argument
 return inlineError (data.crit\_err\_msgs.conflicting, {origin.title, origin.title1});
 end

 ulx.url1.title = args.title or args.title1;

 local rend = createRendering()
 if not rend then
 return inlineError (data.crit\_err\_msgs.unknown);
 end

 return rend .. ((unnamed\_params and inlineRed (err\_warn\_msgs.unnamed\_params, 'warning')) or *) .. createTracking();*

end

--[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------
]]

return {webarchive = webarchive}; – *You Only Live Twice* (70%), *Licence to Kill* (71%) and *The Living Daylights* (73%)(Movies Like For Your Eyes Only.)
63. ↑ Nathan, Ian. For Your Eyes Only. *Empire*.
64. ↑ James Bond's Top 20 (17 November 2006).
65. ↑ Norman Wilner. Rating the Spy Game. MSN.
66. ↑ Sauter, Michael. "Playing the Bond Market", *Entertainment Weekly*, 1 July 2008.
67. ↑ Berardinelli, James (1996). For Your Eyes Only. ReelViews.
68. ↑ Cox, John. The Raymond Benson CBn Interview (Part II). *The Raymond Benson CBn Interview*. CommanderBond.net.
69. ↑ Nashawaty, Chris (12 December 2008). "Moore ... and Sometimes Less". *Entertainment Weekly* (1025). https://www.ew.com/ew/article/0,,20244432,00.html. Retrieved 16 August 2011.
70. ↑ Template:Cite magazine
71. ↑ Template:Cite magazine
72. ↑ Zdyrko, Dave. Top 10 Bond Babes.
73. ↑ Copping, Nicola. "Top 10 most fashionable Bond girls", 1 April 2008.
74. ↑ Awards Search – Best Original Song – Motion Picture. Hollywood Foreign Press Association.
75. ↑ The 54th Academy Awards (1982) Nominees and Winners. *Oscar Legacy*. Academy of Motion Picture Arts and Sciences.
76. ↑ Irving G. Thalberg Award. Academy of Motion Picture Arts and Sciences.
77. ↑  (2006) in Riggs, Thomas: *Contemporary Theatre, Film and Television, Volume 72*. Thomson Gale. ISBN 978-0-7876-9045-8. "Michael G. Wilson. Honors: Best comedy adapted from another medium (with Richard Maibaum), Writers Guild of America, 1982, for For Your Eyes Only"
78. ↑ AFI's 100 Years...100 Songs Nominees.
79. ↑ Jagernauth, Kevin (2015-11-30). Canadian Remake of James Bond Film 'For Your Eyes Only' In The Works, 'Spectre' Followup Starts in Spring (en).
80. ↑ What Does It Mean That James Bond's in the Public Domain in Canada? (en-us).
81. ↑ Pulver, Andrew. "James Bond goes to Ottawa in For Your Eyes Only remake", *The Guardian*, 2015-12-01. (in en)

## Sources

Template:Refbegin

* (2001) *Kiss Kiss Bang! Bang!: the Unofficial James Bond Film Companion*. Batsford Books. ISBN 978-0-7134-8182-2.
* Benson, Raymond (1988). *The James Bond Bedside Companion*. London: Boxtree Ltd. ISBN 1-85283-234-7.
* Black, Jeremy (2004). *Britain Since the Seventies: Politics and Society in the Consumer Age*. Guilford: Biddles Ltd. ISBN 978-1-86189-201-0.
* Black, Jeremy (2005). *The Politics of James Bond: from Fleming's Novel to the Big Screen*. University of Nebraska Press. ISBN 978-0-8032-6240-9.
* Broccoli, Albert R (1998). *When the Snow Melts*. London: Boxtree Ltd. ISBN 978-0-7522-1162-6.
* Chancellor, Henry (2005). *James Bond: The Man and His World*. London: John Murray. ISBN 978-0-7195-6815-2.
* Chaykin, Howard (2011). *Howard Chaykin: Conversations*. UPM. ISBN 978-1-60473-975-6.
* Ciment, Michel (2009). *Film World: The Director's Interviews*, Oxford: Berg Publishers. ISBN 978-1-84520-457-0.
* (2007) *James Bond Encyclopedia*. London: Dorling Kindersley. ISBN 978-1-4053-3427-3.
* Falsetto, Mario (2001). *Stanley Kubrick: A Narrative and Stylistic Analysis*. Praeger. ISBN 978-0-275-97291-2.
* (2000) *The Cinema of Hong Kong*. Cambridge University Press. ISBN 978-0-521-77235-8.
* Lindner, Christoph (2003). *The James Bond Phenomenon: a Critical Reader*. Manchester University Press. ISBN 978-0-7190-6541-5.
* Peary, Danny (1986). *Guide for the Film Fanatic*. Simon & Schuster. ISBN 978-0-671-61081-4.
* (1998) *The Essential Bond*. London: Boxtree Ltd. ISBN 978-0-7522-2477-0.
* Smith, Jim (2002). *Bond Films*. London: Virgin Books. ISBN 978-0-7535-0709-4.
* (2010) *Comic Book Price Guide 2010*. Krause Publications. ISBN 978-1-4402-1399-1.

Template:Refend

## External links

* For Your Eyes Only at Wikipedia

* *For Your Eyes Only* at James Bond Wiki
* *For Your Eyes Only* at the Internet Movie Database
* *For Your Eyes Only* at the TCM Movie Database
* *For Your Eyes Only* at Metro-Goldwyn-Mayer

**Soundtracks:** *Moonraker*